Compare commits
37 Commits
fix/files_
...
fix/ingres
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a0cac57b3 | ||
|
|
fa92825ce9 | ||
|
|
0e04f416d7 | ||
|
|
e43055b0f3 | ||
|
|
f918614bd2 | ||
|
|
12f19b7d46 | ||
|
|
5c8f3ea2ff | ||
|
|
9b7635f244 | ||
|
|
a949e317ac | ||
|
|
f362396514 | ||
|
|
d4a1a44e39 | ||
|
|
95fdffb24f | ||
|
|
4c72114a4d | ||
|
|
e28371551b | ||
|
|
ef01c331e9 | ||
|
|
40b29d12d6 | ||
|
|
506bd3bc1d | ||
|
|
9d097f77b1 | ||
|
|
a71b536a80 | ||
|
|
8eb2d86f56 | ||
|
|
ea0404fe2b | ||
|
|
af8e3b172c | ||
|
|
e00018de59 | ||
|
|
6bba107fdd | ||
|
|
ffb96bcbfc | ||
|
|
a6e4a73af2 | ||
|
|
61d3dedbfd | ||
|
|
962e251691 | ||
|
|
b37adf2521 | ||
|
|
460603ae69 | ||
|
|
1197860c29 | ||
|
|
417c4b520b | ||
|
|
e1fa887e6c | ||
|
|
b2e84cfd21 | ||
|
|
e8f0054b4f | ||
|
|
cd6c89f724 | ||
|
|
3d3d85ca3e |
12
.github/workflows/check.yaml
vendored
12
.github/workflows/check.yaml
vendored
@@ -3,8 +3,12 @@ name: Lint and Test Charts
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "release-*" ]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
pull_request_target:
|
||||
branches: [ "main", "release-*" ]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -55,7 +59,7 @@ jobs:
|
||||
steps:
|
||||
- id: generate
|
||||
run: |
|
||||
v=1.12.0-$(echo $RANDOM)
|
||||
v=1.12.0-$(echo $RANDOM$RANDOM)
|
||||
echo "version=$v" >> "$GITHUB_OUTPUT"
|
||||
|
||||
upload-cli:
|
||||
@@ -99,6 +103,12 @@ jobs:
|
||||
runs-on: [self-hosted, linux, ARM64]
|
||||
|
||||
steps:
|
||||
- name: Install skopeo (Ubuntu)
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y skopeo
|
||||
|
||||
|
||||
- name: 'Checkout source code'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -32,3 +32,8 @@ olares-cli-*.tar.gz
|
||||
cli/output
|
||||
daemon/output
|
||||
daemon/bin
|
||||
|
||||
docs/.vitepress/dist/
|
||||
docs/.vitepress/cache/
|
||||
node_modules
|
||||
.idea/
|
||||
@@ -113,7 +113,7 @@ This section lists the main directories in the Olares repository:
|
||||
* **[`apps`](./apps)**: Contains the code for system applications, primarily for `larepass`.
|
||||
* **[`cli`](./cli)**: Contains the code for `olares-cli`, the command-line interface tool for Olares.
|
||||
* **[`daemon`](./daemon)**: Contains the code for `olaresd`, the system daemon process.
|
||||
* **`docs`**: Contains documentation for the project.
|
||||
* **[`docs`](./docs)**: Contains documentation for the project.
|
||||
* **[`framework`](./framework)**: Contains the Olares system services.
|
||||
* **[`infrastructure`](./infrastructure)**: Contains code related to infrastructure components such as computing, storage, networking, and GPUs.
|
||||
* **[`platform`](./platform)**: Contains code for cloud-native components like databases and message queues.
|
||||
|
||||
@@ -115,7 +115,7 @@ Olares 代码库中的主要目录如下:
|
||||
* **[`apps`](./apps)**: 用于存放系统应用,主要是 `larepass` 的代码。
|
||||
* **[`cli`](./cli)**: 用于存放 `olares-cli`(Olares 的命令行界面工具)的代码。
|
||||
* **[`daemon`](./daemon)**: 用于存放 `olaresd`(系统守护进程)的代码。
|
||||
* **`docs`**: 用于存放 Olares 项目的文档。
|
||||
* **[`docs`**](./docs)**: 用于存放 Olares 项目的文档。
|
||||
* **[`framework`](./framework)**: 用来存放 Olares 系统服务代码。
|
||||
* **[`infrastructure`](./infrastructure)**: 用于存放计算,存储,网络,GPU 等基础设施的代码。
|
||||
* **[`platform`](./platform)**: 用于存放数据库、消息队列等云原生组件的代码。
|
||||
|
||||
@@ -115,7 +115,7 @@ Olaresは以下のLinuxプラットフォームで動作検証を完了してい
|
||||
* **[`apps`](./apps)**: システムアプリケーションのコードが含まれており、主に `larepass` 用です。
|
||||
* **[`cli`](./cli)**: Olares のコマンドラインインターフェースツールである `olares-cli` のコードが含まれています。
|
||||
* **[`daemon`](./daemon)**: システムデーモンプロセスである `olaresd` のコードが含まれています。
|
||||
* **`docs`**: プロジェクトのドキュメントが含まれています。
|
||||
* **[`docs`](./docs)**: プロジェクトのドキュメントが含まれています。
|
||||
* **[`framework`](./framework)**: Olares システムサービスが含まれています。
|
||||
* **[`infrastructure`](./infrastructure)**: コンピューティング、ストレージ、ネットワーキング、GPU などのインフラストラクチャコンポーネントに関連するコードが含まれています。
|
||||
* **[`platform`](./platform)**: データベースやメッセージキューなどのクラウドネイティブコンポーネントのコードが含まれています。
|
||||
|
||||
@@ -345,7 +345,7 @@ spec:
|
||||
- name: PGDB
|
||||
value: user_space_{{ .Values.bfl.username }}_cloud_drive_integration
|
||||
- name: system-frontend-init
|
||||
image: beclab/system-frontend:v1.3.102
|
||||
image: beclab/system-frontend:v1.3.108
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /bin/sh
|
||||
@@ -458,7 +458,7 @@ spec:
|
||||
value: os.market.{{ .Values.bfl.username}}
|
||||
- name: NATS_SUBJECT_APPLICATION
|
||||
value: os.application.{{ .Values.bfl.username}}
|
||||
- name: NATS_SUBJECT_NOTIFICATION
|
||||
- name: NATS_SUBJECT_USER_NOTIFICATION
|
||||
value: os.notification.{{ .Values.bfl.username}}
|
||||
- name: NATS_SUBJECT_FILES
|
||||
value: os.files.{{ .Values.bfl.username}}
|
||||
@@ -467,7 +467,7 @@ spec:
|
||||
- name: NATS_SUBJECT_VAULT
|
||||
value: os.vault.{{ .Values.bfl.username}}
|
||||
- name: user-service
|
||||
image: beclab/user-service:v0.0.21
|
||||
image: beclab/user-service:v0.0.35
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
@@ -525,7 +525,7 @@ spec:
|
||||
value: os.market.{{ .Values.bfl.username}}
|
||||
- name: NATS_SUBJECT_APPLICATION
|
||||
value: os.application.{{ .Values.bfl.username}}
|
||||
- name: NATS_SUBJECT_NOTIFICATION
|
||||
- name: NATS_SUBJECT_USER_NOTIFICATION
|
||||
value: os.notification.{{ .Values.bfl.username}}
|
||||
- name: NATS_SUBJECT_FILES
|
||||
value: os.files.{{ .Values.bfl.username}}
|
||||
@@ -533,13 +533,17 @@ spec:
|
||||
value: os.knowledge.{{ .Values.bfl.username}}
|
||||
- name: NATS_SUBJECT_VAULT
|
||||
value: os.vault.{{ .Values.bfl.username}}
|
||||
- name: NATS_SUBJECT_BACKUP
|
||||
value: os.backup.{{ .Values.bfl.username}}
|
||||
- name: NATS_SUBJECT_INTENT
|
||||
value: os.intent.{{ .Values.bfl.username}}
|
||||
- name: REDIS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
key: redis_password
|
||||
name: user-service-secrets
|
||||
- name: REDIS_HOST
|
||||
value: redis-cluster-proxy.user-system-guotest334
|
||||
value: redis-cluster-proxy.user-system-{{ .Values.bfl.username }}
|
||||
- name: REDIS_PORT
|
||||
value: '6379'
|
||||
|
||||
@@ -2175,5 +2179,13 @@ spec:
|
||||
permission:
|
||||
sub: allow
|
||||
pub: allow
|
||||
- name: "backup.*"
|
||||
permission:
|
||||
sub: allow
|
||||
pub: allow
|
||||
- name: "intent.*"
|
||||
permission:
|
||||
sub: allow
|
||||
pub: allow
|
||||
|
||||
user: user-service-{{ .Values.bfl.username }}
|
||||
|
||||
@@ -29,7 +29,7 @@ spec:
|
||||
|
||||
containers:
|
||||
- name: wizard
|
||||
image: beclab/wizard:v1.3.57
|
||||
image: beclab/wizard:v1.3.103
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
@@ -9,6 +9,21 @@ import json
|
||||
|
||||
CDN_URL = "https://dc3p1870nn3cj.cloudfront.net"
|
||||
|
||||
def get_file_size(objectid, fileid):
|
||||
url = f"{CDN_URL}/{objectid}"
|
||||
try:
|
||||
response = requests.head(url)
|
||||
response.raise_for_status()
|
||||
content_length = response.headers.get('Content-Length')
|
||||
if content_length:
|
||||
return int(content_length)
|
||||
else:
|
||||
print(f"Content-Length header missing for {fileid} from {url}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except requests.RequestException as e:
|
||||
print(f"Error getting file size for {fileid} from {url}: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def download_checksum(name):
|
||||
"""Downloads the checksum for a given name."""
|
||||
url = f"{CDN_URL}/{name}.checksum.txt"
|
||||
@@ -74,13 +89,17 @@ def main():
|
||||
checksum_amd64 = download_checksum(url_amd64)
|
||||
checksum_arm64 = download_checksum(url_arm64)
|
||||
|
||||
file_size_amd64 = get_file_size(url_amd64, fileid)
|
||||
file_size_arm64 = get_file_size(url_arm64, fileid)
|
||||
|
||||
manifest_amd64_data[filename] = {
|
||||
"type": "component",
|
||||
"path": path,
|
||||
"deps": deps,
|
||||
"url_amd64": url_amd64,
|
||||
"checksum_amd64": checksum_amd64,
|
||||
"fileid": fileid
|
||||
"fileid": fileid,
|
||||
"size": file_size_amd64,
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +109,8 @@ def main():
|
||||
"deps": deps,
|
||||
"url_arm64": url_arm64,
|
||||
"checksum_arm64": checksum_arm64,
|
||||
"fileid": fileid
|
||||
"fileid": fileid,
|
||||
"size": file_size_arm64,
|
||||
}
|
||||
|
||||
except FileNotFoundError:
|
||||
@@ -115,6 +135,9 @@ def main():
|
||||
checksum_amd64 = download_checksum(name)
|
||||
checksum_arm64 = download_checksum(f"arm64/{name}")
|
||||
|
||||
file_size_amd64 = get_file_size(url_amd64, line)
|
||||
file_size_arm64 = get_file_size(url_arm64, line)
|
||||
|
||||
# Get the image manifest
|
||||
image_manifest_amd64 = get_image_manifest(name)
|
||||
image_manifest_arm64 = get_image_manifest(f"arm64/{name}")
|
||||
@@ -127,6 +150,7 @@ def main():
|
||||
"url_amd64": url_amd64,
|
||||
"checksum_amd64": checksum_amd64,
|
||||
"fileid": line,
|
||||
"size": file_size_amd64,
|
||||
"manifest": image_manifest_amd64
|
||||
}
|
||||
|
||||
@@ -137,6 +161,7 @@ def main():
|
||||
"url_arm64": url_arm64,
|
||||
"checksum_arm64": checksum_arm64,
|
||||
"fileid": line,
|
||||
"size": file_size_arm64,
|
||||
"manifest": image_manifest_arm64
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ cat $1|while read image; do
|
||||
code=$(curl -o /dev/null -fsSLI -w "%{http_code}" https://dc3p1870nn3cj.cloudfront.net/$path$name.tar.gz)
|
||||
if [ $code -eq 403 ]; then
|
||||
set -ex
|
||||
docker pull $image
|
||||
docker save $image -o $name.tar
|
||||
skopeo copy --insecure-policy docker://$image oci-archive:$name.tar
|
||||
gzip $name.tar
|
||||
|
||||
md5sum $name.tar.gz > $checksum
|
||||
@@ -51,8 +50,7 @@ cat $1|while read image; do
|
||||
code=$(curl -o /dev/null -fsSLI -w "%{http_code}" https://dc3p1870nn3cj.cloudfront.net/$path$checksum)
|
||||
if [ $code -eq 403 ]; then
|
||||
set -ex
|
||||
docker pull $image
|
||||
docker save $image -o $name.tar
|
||||
skopeo copy --insecure-policy docker://$image oci-archive:$name.tar
|
||||
gzip $name.tar
|
||||
|
||||
md5sum $name.tar.gz > $checksum
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/beclab/Olares/cli/cmd/ctl/options"
|
||||
"github.com/beclab/Olares/cli/pkg/phase"
|
||||
"github.com/beclab/Olares/cli/pkg/pipelines"
|
||||
"github.com/beclab/Olares/cli/pkg/upgrade"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"log"
|
||||
"os"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type UpgradeOsOptions struct {
|
||||
@@ -31,6 +38,49 @@ func NewCmdUpgradeOs() *cobra.Command {
|
||||
}
|
||||
o.UpgradeOptions.AddFlags(cmd)
|
||||
cmd.AddCommand(NewCmdUpgradePrecheck())
|
||||
cmd.AddCommand(NewCmdGetUpgradePath())
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewCmdGetUpgradePath() *cobra.Command {
|
||||
var baseVersionStr string
|
||||
var latestFirst bool
|
||||
cmd := &cobra.Command{
|
||||
Use: "path",
|
||||
Short: "Get the upgrade path (required intermediate versions) from base version to the latest upgradable version (as known to this release of olares-cli)",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var baseVersion *semver.Version
|
||||
var err error
|
||||
if baseVersionStr == "" {
|
||||
baseVersionStr, err = phase.GetOlaresVersion()
|
||||
if err != nil {
|
||||
return errors.New("failed to get current Olares version, please specify the base version explicitly")
|
||||
}
|
||||
}
|
||||
baseVersion, err = semver.NewVersion(baseVersionStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid base version: %v", err)
|
||||
}
|
||||
|
||||
path, err := upgrade.GetUpgradePathFor(baseVersion, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if latestFirst {
|
||||
slices.Reverse(path)
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(cmd.OutOrStdout())
|
||||
encoder.SetIndent("", " ")
|
||||
return encoder.Encode(path)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&baseVersionStr, "base-version", "b", baseVersionStr, "base version to be upgraded, defaults to the current Olares version if inside Olares cluster")
|
||||
cmd.Flags().BoolVar(&latestFirst, "latest-first", true, "sort versions to put recent ones in the front")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
32
cli/go.mod
32
cli/go.mod
@@ -46,16 +46,16 @@ require (
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.37.0
|
||||
golang.org/x/term v0.31.0
|
||||
golang.org/x/text v0.24.0
|
||||
golang.org/x/crypto v0.39.0
|
||||
golang.org/x/term v0.32.0
|
||||
golang.org/x/text v0.26.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
helm.sh/helm/v3 v3.18.1
|
||||
k8s.io/api v0.33.0
|
||||
k8s.io/apimachinery v0.33.0
|
||||
k8s.io/cli-runtime v0.33.0
|
||||
k8s.io/client-go v0.33.0
|
||||
k8s.io/kubectl v0.33.0
|
||||
helm.sh/helm/v3 v3.18.4
|
||||
k8s.io/api v0.33.2
|
||||
k8s.io/apimachinery v0.33.2
|
||||
k8s.io/cli-runtime v0.33.2
|
||||
k8s.io/client-go v0.33.2
|
||||
k8s.io/kubectl v0.33.2
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
|
||||
sigs.k8s.io/controller-runtime v0.21.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.19.0
|
||||
@@ -191,9 +191,9 @@ require (
|
||||
go.opentelemetry.io/otel v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.33.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.28.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
@@ -202,13 +202,13 @@ require (
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.33.0 // indirect
|
||||
k8s.io/apiserver v0.33.0 // indirect
|
||||
k8s.io/component-base v0.33.0 // indirect
|
||||
k8s.io/component-helpers v0.33.0 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.33.2 // indirect
|
||||
k8s.io/apiserver v0.33.2 // indirect
|
||||
k8s.io/component-base v0.33.2 // indirect
|
||||
k8s.io/component-helpers v0.33.2 // indirect
|
||||
k8s.io/cri-api v0.27.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
oras.land/oras-go/v2 v2.5.0 // indirect
|
||||
oras.land/oras-go/v2 v2.6.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.19.0 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
|
||||
72
cli/go.sum
72
cli/go.sum
@@ -538,8 +538,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@@ -549,8 +549,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -564,8 +564,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
@@ -575,8 +575,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -602,14 +602,14 @@ golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -622,8 +622,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -678,38 +678,38 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
helm.sh/helm/v3 v3.18.1 h1:qLhXmtqXOHQb0Xv9HJolOLlah8RWbgyzt50xrtTWAlg=
|
||||
helm.sh/helm/v3 v3.18.1/go.mod h1:43QHS1W97RcoFJRk36ZBhHdTfykqBlJdsWp3yhzdq8w=
|
||||
helm.sh/helm/v3 v3.18.4 h1:pNhnHM3nAmDrxz6/UC+hfjDY4yeDATQCka2/87hkZXQ=
|
||||
helm.sh/helm/v3 v3.18.4/go.mod h1:WVnwKARAw01iEdjpEkP7Ii1tT1pTPYfM1HsakFKM3LI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
|
||||
k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM=
|
||||
k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs=
|
||||
k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc=
|
||||
k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
|
||||
k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc=
|
||||
k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8=
|
||||
k8s.io/cli-runtime v0.33.0 h1:Lbl/pq/1o8BaIuyn+aVLdEPHVN665tBAXUePs8wjX7c=
|
||||
k8s.io/cli-runtime v0.33.0/go.mod h1:QcA+r43HeUM9jXFJx7A+yiTPfCooau/iCcP1wQh4NFw=
|
||||
k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98=
|
||||
k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg=
|
||||
k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk=
|
||||
k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU=
|
||||
k8s.io/component-helpers v0.33.0 h1:0AdW0A0mIgljLgtG0hJDdJl52PPqTrtMgOgtm/9i/Ys=
|
||||
k8s.io/component-helpers v0.33.0/go.mod h1:9SRiXfLldPw9lEEuSsapMtvT8j/h1JyFFapbtybwKvU=
|
||||
k8s.io/api v0.33.2 h1:YgwIS5jKfA+BZg//OQhkJNIfie/kmRsO0BmNaVSimvY=
|
||||
k8s.io/api v0.33.2/go.mod h1:fhrbphQJSM2cXzCWgqU29xLDuks4mu7ti9vveEnpSXs=
|
||||
k8s.io/apiextensions-apiserver v0.33.2 h1:6gnkIbngnaUflR3XwE1mCefN3YS8yTD631JXQhsU6M8=
|
||||
k8s.io/apiextensions-apiserver v0.33.2/go.mod h1:IvVanieYsEHJImTKXGP6XCOjTwv2LUMos0YWc9O+QP8=
|
||||
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
|
||||
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/apiserver v0.33.2 h1:KGTRbxn2wJagJowo29kKBp4TchpO1DRO3g+dB/KOJN4=
|
||||
k8s.io/apiserver v0.33.2/go.mod h1:9qday04wEAMLPWWo9AwqCZSiIn3OYSZacDyu/AcoM/M=
|
||||
k8s.io/cli-runtime v0.33.2 h1:koNYQKSDdq5AExa/RDudXMhhtFasEg48KLS2KSAU74Y=
|
||||
k8s.io/cli-runtime v0.33.2/go.mod h1:gnhsAWpovqf1Zj5YRRBBU7PFsRc6NkEkwYNQE+mXL88=
|
||||
k8s.io/client-go v0.33.2 h1:z8CIcc0P581x/J1ZYf4CNzRKxRvQAwoAolYPbtQes+E=
|
||||
k8s.io/client-go v0.33.2/go.mod h1:9mCgT4wROvL948w6f6ArJNb7yQd7QsvqavDeZHvNmHo=
|
||||
k8s.io/component-base v0.33.2 h1:sCCsn9s/dG3ZrQTX/Us0/Sx2R0G5kwa0wbZFYoVp/+0=
|
||||
k8s.io/component-base v0.33.2/go.mod h1:/41uw9wKzuelhN+u+/C59ixxf4tYQKW7p32ddkYNe2k=
|
||||
k8s.io/component-helpers v0.33.2 h1:AjCtYzst11NV8ensxV/2LEEXRwctqS7Bs44bje9Qcnw=
|
||||
k8s.io/component-helpers v0.33.2/go.mod h1:PsPpiCk74n8pGWp1d6kjK/iSKBTyQfIacv02BNkMenU=
|
||||
k8s.io/cri-api v0.27.1 h1:KWO+U8MfI9drXB/P4oU9VchaWYOlwDglJZVHWMpTT3Q=
|
||||
k8s.io/cri-api v0.27.1/go.mod h1:+Ts/AVYbIo04S86XbTD73UPp/DkTiYxtsFeOFEu32L0=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/kubectl v0.33.0 h1:HiRb1yqibBSCqic4pRZP+viiOBAnIdwYDpzUFejs07g=
|
||||
k8s.io/kubectl v0.33.0/go.mod h1:gAlGBuS1Jq1fYZ9AjGWbI/5Vk3M/VW2DK4g10Fpyn/0=
|
||||
k8s.io/kubectl v0.33.2 h1:7XKZ6DYCklu5MZQzJe+CkCjoGZwD1wWl7t/FxzhMz7Y=
|
||||
k8s.io/kubectl v0.33.2/go.mod h1:8rC67FB8tVTYraovAGNi/idWIK90z2CHFNMmGJZJ3KI=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c=
|
||||
oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg=
|
||||
oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc=
|
||||
oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o=
|
||||
sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8=
|
||||
sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
|
||||
@@ -327,7 +327,7 @@ func (a *Argument) SaveReleaseInfo() error {
|
||||
ENV_OLARES_VERSION: a.OlaresVersion,
|
||||
}
|
||||
|
||||
if a.User != nil {
|
||||
if a.User != nil && a.User.UserName != "" && a.User.DomainName != "" {
|
||||
releaseInfoMap["OLARES_NAME"] = fmt.Sprintf("%s@%s", a.User.UserName, a.User.DomainName)
|
||||
} else {
|
||||
if util.IsExist(OlaresReleaseFile) {
|
||||
|
||||
@@ -92,6 +92,12 @@ func (i *InstallTerminusdBinaryModule) Init() {
|
||||
i.Name = "InstallOlaresdBinaryModule"
|
||||
i.Desc = "Install olaresd"
|
||||
|
||||
updateHost := &task.LocalTask{
|
||||
Name: "UpdateHosts",
|
||||
Action: new(terminus.UpdateKubeKeyHosts),
|
||||
Prepare: new(HostnameNotResolvable),
|
||||
}
|
||||
|
||||
install := &task.RemoteTask{
|
||||
Name: "InstallOlaresdBinary",
|
||||
Desc: "Install olaresd using binary",
|
||||
@@ -134,6 +140,7 @@ func (i *InstallTerminusdBinaryModule) Init() {
|
||||
}
|
||||
|
||||
i.Tasks = []task.Interface{
|
||||
updateHost,
|
||||
install,
|
||||
generateEnv,
|
||||
generateService,
|
||||
|
||||
21
cli/pkg/daemon/prepares.go
Normal file
21
cli/pkg/daemon/prepares.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
"net"
|
||||
)
|
||||
|
||||
type HostnameNotResolvable struct {
|
||||
common.KubePrepare
|
||||
}
|
||||
|
||||
func (p *HostnameNotResolvable) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
ips, _ := net.LookupIP(runtime.GetSystemInfo().GetHostname())
|
||||
for _, ip := range ips {
|
||||
if ip.To4() != nil && ip.To4().String() == runtime.GetSystemInfo().GetLocalIp() {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -2,11 +2,10 @@ package pipelines
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/upgrade"
|
||||
"github.com/beclab/Olares/cli/pkg/utils"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/beclab/Olares/cli/cmd/ctl/options"
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
@@ -40,9 +39,23 @@ func UpgradeOlaresPipeline(opts *options.UpgradeOptions) error {
|
||||
return fmt.Errorf("error parsing target Olares version: %v", err)
|
||||
}
|
||||
|
||||
if !targetVersion.GreaterThan(currentVersion) {
|
||||
fmt.Printf("current version is: %s, no need to upgrade to %s\n", currentVersion.String(), opts.Version)
|
||||
os.Exit(0)
|
||||
upgradePath, err := upgrade.GetUpgradePathFor(currentVersion, targetVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(upgradePath) > 1 {
|
||||
fmt.Printf("unable to upgrade from %s to %s directly,\n", currentVersion, targetVersion)
|
||||
if len(upgradePath) == 2 {
|
||||
fmt.Printf("please upgrade to %s first!\n", upgradePath[0])
|
||||
} else {
|
||||
line := "please upgrade sequentially to:"
|
||||
for _, u := range upgradePath[:len(upgradePath)-1] {
|
||||
line += fmt.Sprintf(" %s", u)
|
||||
}
|
||||
line += " first!"
|
||||
fmt.Println(line)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
arg := common.NewArgument()
|
||||
@@ -59,9 +72,8 @@ func UpgradeOlaresPipeline(opts *options.UpgradeOptions) error {
|
||||
manifest := path.Join(runtime.GetInstallerDir(), "installation.manifest")
|
||||
runtime.Arg.SetManifest(manifest)
|
||||
|
||||
upgradeModule := &upgrade.UpgradeModule{
|
||||
CurrentVersion: currentVersion,
|
||||
TargetVersion: targetVersion,
|
||||
upgradeModule := &upgrade.Module{
|
||||
TargetVersion: targetVersion,
|
||||
}
|
||||
|
||||
p := &pipeline.Pipeline{
|
||||
|
||||
94
cli/pkg/upgrade/1_12_0_20250702.go
Normal file
94
cli/pkg/upgrade/1_12_0_20250702.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package upgrade
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
"github.com/beclab/Olares/cli/pkg/core/logger"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type upgrader_1_12_0_20250702 struct {
|
||||
upgraderBase
|
||||
}
|
||||
|
||||
func (u upgrader_1_12_0_20250702) Version() *semver.Version {
|
||||
return semver.MustParse("1.12.0-20250702")
|
||||
}
|
||||
|
||||
func (u upgrader_1_12_0_20250702) PrepareForUpgrade() []task.Interface {
|
||||
preTasks := []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "UpdateSysctlReservedPorts",
|
||||
Action: new(updateSysctlReservedPorts),
|
||||
},
|
||||
}
|
||||
return append(preTasks, u.upgraderBase.PrepareForUpgrade()...)
|
||||
}
|
||||
|
||||
type updateSysctlReservedPorts struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (u *updateSysctlReservedPorts) Execute(runtime connector.Runtime) error {
|
||||
const sysctlFile = "/etc/sysctl.conf"
|
||||
const reservedPortsKey = "net.ipv4.ip_local_reserved_ports"
|
||||
const expectedValue = "30000-32767,46800-50000"
|
||||
|
||||
content, err := os.ReadFile(sysctlFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read sysctl.conf: %v", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(content), "\n")
|
||||
var foundKey bool
|
||||
var needUpdate bool
|
||||
var updatedLines []string
|
||||
|
||||
for _, line := range lines {
|
||||
trimmedLine := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmedLine, reservedPortsKey) {
|
||||
foundKey = true
|
||||
parts := strings.SplitN(trimmedLine, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
currentValue := strings.TrimSpace(parts[1])
|
||||
if currentValue != expectedValue {
|
||||
logger.Infof("updating %s from %s to %s", reservedPortsKey, currentValue, expectedValue)
|
||||
updatedLines = append(updatedLines, fmt.Sprintf("%s=%s", reservedPortsKey, expectedValue))
|
||||
needUpdate = true
|
||||
} else {
|
||||
updatedLines = append(updatedLines, line)
|
||||
}
|
||||
} else {
|
||||
updatedLines = append(updatedLines, line)
|
||||
}
|
||||
} else {
|
||||
updatedLines = append(updatedLines, line)
|
||||
}
|
||||
}
|
||||
|
||||
if !foundKey {
|
||||
logger.Infof("key %s not found in sysctl.conf, adding it", reservedPortsKey)
|
||||
updatedLines = append(updatedLines, fmt.Sprintf("%s=%s", reservedPortsKey, expectedValue))
|
||||
needUpdate = true
|
||||
}
|
||||
|
||||
if needUpdate {
|
||||
updatedContent := strings.Join(updatedLines, "\n")
|
||||
if err := os.WriteFile(sysctlFile, []byte(updatedContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write updated sysctl.conf: %v", err)
|
||||
}
|
||||
|
||||
if _, err := runtime.GetRunner().SudoCmd("sysctl -p", false, false); err != nil {
|
||||
return fmt.Errorf("failed to reload sysctl: %v", err)
|
||||
}
|
||||
logger.Infof("updated and reloaded sysctl configuration")
|
||||
} else {
|
||||
logger.Debugf("%s already has the expected value: %s", reservedPortsKey, expectedValue)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -3,9 +3,10 @@ package upgrade
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
"github.com/beclab/Olares/cli/pkg/terminus"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
@@ -20,11 +21,101 @@ import (
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
type PrepareUserInfoForUpgrade struct {
|
||||
// upgraderBase is the general-purpose upgrader implementation
|
||||
// for upgrading across versions without any breaking changes.
|
||||
// Other implementations of breakingUpgrader,
|
||||
// targeted for versions with breaking changes,
|
||||
// should use this as a base for injecting and/or rewriting specific tasks as needed
|
||||
type upgraderBase struct{}
|
||||
|
||||
func (u upgraderBase) PrepareForUpgrade() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "prepareUserInfoForUpgrade",
|
||||
Action: new(prepareUserInfoForUpgrade),
|
||||
Retry: 5,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) ClearAppChartValues() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "ClearAppChartValues",
|
||||
Action: new(terminus.ClearAppValues),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) ClearBFLChartValues() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "ClearBFLChartValues",
|
||||
Action: new(terminus.ClearBFLValues),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) UpdateChartsInAppService() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "UpdateChartsInAppService",
|
||||
Action: new(terminus.CopyAppServiceHelmFiles),
|
||||
Retry: 5,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) UpgradeUserComponents() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "upgradeUserComponents",
|
||||
Action: new(upgradeUserComponents),
|
||||
Retry: 5,
|
||||
Delay: 15 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) UpdateReleaseFile() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "UpdateReleaseFile",
|
||||
Action: new(terminus.WriteReleaseFile),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) UpgradeSystemComponents() []task.Interface {
|
||||
// this task updates the version in the CR
|
||||
// so put this at last to make the whole pipeline
|
||||
// reentrant
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "upgradeSystemComponents",
|
||||
Action: new(upgradeSystemComponents),
|
||||
Retry: 10,
|
||||
Delay: 15 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (u upgraderBase) PostUpgrade() []task.Interface {
|
||||
return []task.Interface{
|
||||
&task.LocalTask{
|
||||
Name: "EnsurePodsUpAndRunningAgain",
|
||||
Action: new(terminus.CheckKeyPodsRunning),
|
||||
Delay: 15 * time.Second,
|
||||
Retry: 60,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type prepareUserInfoForUpgrade struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (p *PrepareUserInfoForUpgrade) Execute(runtime connector.Runtime) error {
|
||||
func (p *prepareUserInfoForUpgrade) Execute(runtime connector.Runtime) error {
|
||||
config, err := ctrl.GetConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get rest config: %s", err)
|
||||
@@ -63,7 +154,7 @@ func (p *PrepareUserInfoForUpgrade) Execute(runtime connector.Runtime) error {
|
||||
return fmt.Errorf("failed to get user-space-%x: %v", user.Name, err)
|
||||
}
|
||||
usersToUpgrade = append(usersToUpgrade, user)
|
||||
if role, ok := user.Annotations["bytetrade.io/owner-role"]; ok && role == "platform-admin" {
|
||||
if role, ok := user.Annotations["bytetrade.io/owner-role"]; ok && role == "owner" {
|
||||
adminUser = user.Name
|
||||
}
|
||||
}
|
||||
@@ -79,11 +170,11 @@ func (p *PrepareUserInfoForUpgrade) Execute(runtime connector.Runtime) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpgradeUserComponents struct {
|
||||
type upgradeUserComponents struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (u *UpgradeUserComponents) Execute(runtime connector.Runtime) error {
|
||||
func (u *upgradeUserComponents) Execute(runtime connector.Runtime) error {
|
||||
config, err := ctrl.GetConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get rest config: %s", err)
|
||||
@@ -178,11 +269,11 @@ func (u *UpgradeUserComponents) Execute(runtime connector.Runtime) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpgradeSystemComponents struct {
|
||||
type upgradeSystemComponents struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (u *UpgradeSystemComponents) Execute(runtime connector.Runtime) error {
|
||||
func (u *upgradeSystemComponents) Execute(runtime connector.Runtime) error {
|
||||
config, err := ctrl.GetConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get rest config: %s", err)
|
||||
@@ -222,67 +313,3 @@ func (u *UpgradeSystemComponents) Execute(runtime connector.Runtime) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateSysctlReservedPorts struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (u *UpdateSysctlReservedPorts) Execute(runtime connector.Runtime) error {
|
||||
const sysctlFile = "/etc/sysctl.conf"
|
||||
const reservedPortsKey = "net.ipv4.ip_local_reserved_ports"
|
||||
const expectedValue = "30000-32767,46800-50000"
|
||||
|
||||
content, err := os.ReadFile(sysctlFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read sysctl.conf: %v", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(content), "\n")
|
||||
var foundKey bool
|
||||
var needUpdate bool
|
||||
var updatedLines []string
|
||||
|
||||
for _, line := range lines {
|
||||
trimmedLine := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmedLine, reservedPortsKey) {
|
||||
foundKey = true
|
||||
parts := strings.SplitN(trimmedLine, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
currentValue := strings.TrimSpace(parts[1])
|
||||
if currentValue != expectedValue {
|
||||
logger.Infof("updating %s from %s to %s", reservedPortsKey, currentValue, expectedValue)
|
||||
updatedLines = append(updatedLines, fmt.Sprintf("%s=%s", reservedPortsKey, expectedValue))
|
||||
needUpdate = true
|
||||
} else {
|
||||
updatedLines = append(updatedLines, line)
|
||||
}
|
||||
} else {
|
||||
updatedLines = append(updatedLines, line)
|
||||
}
|
||||
} else {
|
||||
updatedLines = append(updatedLines, line)
|
||||
}
|
||||
}
|
||||
|
||||
if !foundKey {
|
||||
logger.Infof("key %s not found in sysctl.conf, adding it", reservedPortsKey)
|
||||
updatedLines = append(updatedLines, fmt.Sprintf("%s=%s", reservedPortsKey, expectedValue))
|
||||
needUpdate = true
|
||||
}
|
||||
|
||||
if needUpdate {
|
||||
updatedContent := strings.Join(updatedLines, "\n")
|
||||
if err := os.WriteFile(sysctlFile, []byte(updatedContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write updated sysctl.conf: %v", err)
|
||||
}
|
||||
|
||||
if _, err := runtime.GetRunner().SudoCmd("sysctl -p", false, false); err != nil {
|
||||
return fmt.Errorf("failed to reload sysctl: %v", err)
|
||||
}
|
||||
logger.Infof("updated and reloaded sysctl configuration")
|
||||
} else {
|
||||
logger.Debugf("%s already has the expected value: %s", reservedPortsKey, expectedValue)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
22
cli/pkg/upgrade/interfaces.go
Normal file
22
cli/pkg/upgrade/interfaces.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package upgrade
|
||||
|
||||
import (
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
)
|
||||
|
||||
type upgrader interface {
|
||||
PrepareForUpgrade() []task.Interface
|
||||
ClearAppChartValues() []task.Interface
|
||||
ClearBFLChartValues() []task.Interface
|
||||
UpdateChartsInAppService() []task.Interface
|
||||
UpgradeUserComponents() []task.Interface
|
||||
UpdateReleaseFile() []task.Interface
|
||||
UpgradeSystemComponents() []task.Interface
|
||||
PostUpgrade() []task.Interface
|
||||
}
|
||||
|
||||
type breakingUpgrader interface {
|
||||
upgrader
|
||||
Version() *semver.Version
|
||||
}
|
||||
51
cli/pkg/upgrade/modules.go
Normal file
51
cli/pkg/upgrade/modules.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package upgrade
|
||||
|
||||
import (
|
||||
"github.com/beclab/Olares/cli/pkg/bootstrap/precheck"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
common.KubeModule
|
||||
TargetVersion *semver.Version
|
||||
}
|
||||
|
||||
func (m *Module) Init() {
|
||||
m.Name = "UpgradeOlares"
|
||||
|
||||
u := getUpgraderByVersion(m.TargetVersion)
|
||||
m.Tasks = append(m.Tasks, u.PrepareForUpgrade()...)
|
||||
m.Tasks = append(m.Tasks, u.ClearAppChartValues()...)
|
||||
m.Tasks = append(m.Tasks, u.ClearBFLChartValues()...)
|
||||
m.Tasks = append(m.Tasks, u.UpdateChartsInAppService()...)
|
||||
m.Tasks = append(m.Tasks, u.UpgradeUserComponents()...)
|
||||
m.Tasks = append(m.Tasks, u.UpdateReleaseFile()...)
|
||||
m.Tasks = append(m.Tasks, u.UpgradeSystemComponents()...)
|
||||
m.Tasks = append(m.Tasks, u.PostUpgrade()...)
|
||||
}
|
||||
|
||||
type PrecheckModule struct {
|
||||
common.KubeModule
|
||||
}
|
||||
|
||||
func (m *PrecheckModule) Init() {
|
||||
m.Name = "UpgradePrecheck"
|
||||
|
||||
checkers := []precheck.Checker{
|
||||
new(precheck.MasterNodeReadyCheck),
|
||||
new(precheck.RootPartitionAvailableSpaceCheck),
|
||||
}
|
||||
runPreChecks := &task.LocalTask{
|
||||
Name: "UpgradePrecheck",
|
||||
Action: &precheck.RunChecks{
|
||||
Checkers: checkers,
|
||||
},
|
||||
}
|
||||
|
||||
m.Tasks = []task.Interface{
|
||||
runPreChecks,
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
package upgrade
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/bootstrap/precheck"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
"github.com/beclab/Olares/cli/pkg/terminus"
|
||||
)
|
||||
|
||||
type UpgradeModule struct {
|
||||
common.KubeModule
|
||||
CurrentVersion *semver.Version
|
||||
TargetVersion *semver.Version
|
||||
}
|
||||
|
||||
var (
|
||||
preTasks = []*upgradeTask{
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "UpdateSysctlReservedPorts",
|
||||
Action: new(UpdateSysctlReservedPorts),
|
||||
},
|
||||
Current: &explicitVersionMatcher{max: semver.New(1, 12, 0, "20250701", "")},
|
||||
Target: anyVersion,
|
||||
},
|
||||
}
|
||||
|
||||
coreTasks = []*upgradeTask{
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "PrepareUserInfoForUpgrade",
|
||||
Action: new(PrepareUserInfoForUpgrade),
|
||||
Retry: 5,
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "ClearAppChartValues",
|
||||
Action: new(terminus.ClearAppValues),
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "ClearBFLChartValues",
|
||||
Action: new(terminus.ClearBFLValues),
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "UpdateChartsInAppService",
|
||||
Action: new(terminus.CopyAppServiceHelmFiles),
|
||||
Retry: 5,
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "UpgradeUserComponents",
|
||||
Action: new(UpgradeUserComponents),
|
||||
Retry: 5,
|
||||
Delay: 15 * time.Second,
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "UpdateReleaseFile",
|
||||
Action: new(terminus.WriteReleaseFile),
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
// this task updates the version in the CR
|
||||
// so put this at last to make the whole pipeline
|
||||
// reentrant
|
||||
// maybe it should be put at the last of post tasks
|
||||
// when post tasks are actually needed
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "UpgradeSystemComponents",
|
||||
Action: new(UpgradeSystemComponents),
|
||||
Retry: 10,
|
||||
Delay: 15 * time.Second,
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
{
|
||||
Task: &task.LocalTask{
|
||||
Name: "EnsurePodsUpAndRunningAgain",
|
||||
Action: new(terminus.CheckKeyPodsRunning),
|
||||
Delay: 15 * time.Second,
|
||||
Retry: 60,
|
||||
},
|
||||
Current: atLeasVersion112,
|
||||
Target: atLeasVersion112,
|
||||
},
|
||||
}
|
||||
|
||||
postTasks []*upgradeTask
|
||||
)
|
||||
|
||||
func (m *UpgradeModule) Init() {
|
||||
m.Name = "UpgradeOlares"
|
||||
|
||||
// calculate tasks based on version difference
|
||||
tasks := m.calculateUpgradeTasks()
|
||||
|
||||
m.Tasks = tasks
|
||||
}
|
||||
|
||||
func (m *UpgradeModule) calculateUpgradeTasks() []task.Interface {
|
||||
var tasks []task.Interface
|
||||
|
||||
// for now, tasks are grouped into pre-upgrade/core-upgrade/post-upgrade tasks
|
||||
// only for business logic compatibility
|
||||
// they are still a normal sequence of tasks to be executed
|
||||
// for the module layer
|
||||
tasks = append(tasks, m.calculatePreUpgradeTasks()...)
|
||||
tasks = append(tasks, m.calculateCoreUpgradeTasks()...)
|
||||
tasks = append(tasks, m.calculatePostUpgradeTasks()...)
|
||||
|
||||
return tasks
|
||||
}
|
||||
|
||||
func (m *UpgradeModule) getTasksToExecute(unfiltered []*upgradeTask) []task.Interface {
|
||||
var filtered []task.Interface
|
||||
for _, t := range unfiltered {
|
||||
if t.Match(m.CurrentVersion, m.TargetVersion) {
|
||||
filtered = append(filtered, t.Task)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func (m *UpgradeModule) calculatePreUpgradeTasks() []task.Interface {
|
||||
return m.getTasksToExecute(preTasks)
|
||||
}
|
||||
|
||||
func (m *UpgradeModule) calculateCoreUpgradeTasks() []task.Interface {
|
||||
return m.getTasksToExecute(coreTasks)
|
||||
}
|
||||
|
||||
func (m *UpgradeModule) calculatePostUpgradeTasks() []task.Interface {
|
||||
return m.getTasksToExecute(postTasks)
|
||||
}
|
||||
|
||||
type PrecheckModule struct {
|
||||
common.KubeModule
|
||||
}
|
||||
|
||||
func (m *PrecheckModule) Init() {
|
||||
m.Name = "UpgradePrecheck"
|
||||
|
||||
checkers := []precheck.Checker{
|
||||
new(precheck.MasterNodeReadyCheck),
|
||||
new(precheck.RootPartitionAvailableSpaceCheck),
|
||||
}
|
||||
runPreChecks := &task.LocalTask{
|
||||
Name: "UpgradePrecheck",
|
||||
Action: &precheck.RunChecks{
|
||||
Checkers: checkers,
|
||||
},
|
||||
}
|
||||
|
||||
m.Tasks = []task.Interface{
|
||||
runPreChecks,
|
||||
}
|
||||
}
|
||||
@@ -1,61 +1,166 @@
|
||||
package upgrade
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
"github.com/beclab/Olares/cli/pkg/utils"
|
||||
"github.com/beclab/Olares/cli/version"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// versionMatcher checks if the specified version matches its condition
|
||||
type versionMatcher interface {
|
||||
Match(version *semver.Version) bool
|
||||
}
|
||||
type releaseLine string
|
||||
|
||||
// explicitVersionMatcher matches the specified version by a range of explicitly
|
||||
// set version range by min/max version
|
||||
// and additionally explicitly included/excluded versions
|
||||
// if any type of condition is not set, that check is omitted
|
||||
// i.e., if min is not set, there's no limit on the minimum version
|
||||
// and if no condition is set, the matcher matches all non-nil versions
|
||||
type explicitVersionMatcher struct {
|
||||
min *semver.Version
|
||||
max *semver.Version
|
||||
include []*semver.Version
|
||||
exclude []*semver.Version
|
||||
}
|
||||
var (
|
||||
mainLine = releaseLine("main")
|
||||
dailyLine = releaseLine("daily")
|
||||
|
||||
func (m *explicitVersionMatcher) Match(version *semver.Version) bool {
|
||||
if version == nil {
|
||||
return false
|
||||
dailyUpgraders = []breakingUpgrader{
|
||||
upgrader_1_12_0_20250702{},
|
||||
}
|
||||
for _, v := range m.include {
|
||||
if v.Equal(version) {
|
||||
mainUpgraders = []breakingUpgrader{}
|
||||
)
|
||||
|
||||
func getReleaseLineOfVersion(v *semver.Version) releaseLine {
|
||||
preRelease := v.Prerelease()
|
||||
mainLinePrereleasePrefixes := []string{"alpha", "beta", "rc"}
|
||||
if preRelease == "" {
|
||||
return mainLine
|
||||
}
|
||||
for _, prefix := range mainLinePrereleasePrefixes {
|
||||
if strings.HasPrefix(preRelease, prefix) {
|
||||
return mainLine
|
||||
}
|
||||
}
|
||||
return dailyLine
|
||||
}
|
||||
|
||||
func check(base *semver.Version, target *semver.Version) error {
|
||||
if base == nil {
|
||||
return fmt.Errorf("base version is nil")
|
||||
}
|
||||
baseReleaseLine := getReleaseLineOfVersion(base)
|
||||
|
||||
cliVersion, err := utils.ParseOlaresVersionString(version.VERSION)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid olares-cli version :\"%s\"", version.VERSION)
|
||||
}
|
||||
cliReleaseLine := getReleaseLineOfVersion(cliVersion)
|
||||
if baseReleaseLine != cliReleaseLine {
|
||||
return fmt.Errorf("incompatible base release line: %s and olares-cli release line: %s", baseReleaseLine, cliReleaseLine)
|
||||
}
|
||||
|
||||
if target != nil {
|
||||
if !target.GreaterThan(base) {
|
||||
return fmt.Errorf("base version: %s, target version: %s, no need to upgrade", base, target)
|
||||
}
|
||||
|
||||
targetReleaseLine := getReleaseLineOfVersion(target)
|
||||
if targetReleaseLine != baseReleaseLine {
|
||||
return fmt.Errorf("unable to upgrade to %s on %s release line from %s on %s release line", target, targetReleaseLine, base, baseReleaseLine)
|
||||
}
|
||||
switch targetReleaseLine {
|
||||
case mainLine:
|
||||
if !sameMajorLevelVersion(base, target) {
|
||||
return fmt.Errorf("upgrade on %s rlease line can only be performed across same major level version", baseReleaseLine)
|
||||
}
|
||||
case dailyLine:
|
||||
if !samePatchLevelVersion(base, target) {
|
||||
return fmt.Errorf("upgrade on %s rlease line can only be performed across same patch version", baseReleaseLine)
|
||||
}
|
||||
}
|
||||
|
||||
if target.GreaterThan(cliVersion) {
|
||||
return fmt.Errorf("target version: %s, cli version: %s, please upgrade olares-cli first!", target, cliVersion)
|
||||
}
|
||||
}
|
||||
|
||||
if base.GreaterThan(cliVersion) {
|
||||
return fmt.Errorf("base version: %s, cli version: %s, please upgrade olares-cli first!", base, cliVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetUpgradePathFor(base *semver.Version, target *semver.Version) ([]*semver.Version, error) {
|
||||
if err := check(base, target); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var path []*semver.Version
|
||||
var releaseLineUpgraders []breakingUpgrader
|
||||
var versionFilter func(v *semver.Version) bool
|
||||
switch getReleaseLineOfVersion(base) {
|
||||
case mainLine:
|
||||
releaseLineUpgraders = mainUpgraders
|
||||
versionFilter = func(v *semver.Version) bool {
|
||||
if !v.GreaterThan(base) {
|
||||
return false
|
||||
}
|
||||
if !sameMajorLevelVersion(v, base) {
|
||||
return false
|
||||
}
|
||||
if target != nil && !v.LessThan(target) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
case dailyLine:
|
||||
if target == nil {
|
||||
cliVersion, err := utils.ParseOlaresVersionString(version.VERSION)
|
||||
if err != nil {
|
||||
return path, fmt.Errorf("invalid olares-cli version :\"%s\"", version.VERSION)
|
||||
}
|
||||
if getReleaseLineOfVersion(cliVersion) == dailyLine && samePatchLevelVersion(cliVersion, base) && cliVersion.GreaterThan(base) {
|
||||
target = cliVersion
|
||||
}
|
||||
}
|
||||
releaseLineUpgraders = dailyUpgraders
|
||||
versionFilter = func(v *semver.Version) bool {
|
||||
if !v.GreaterThan(base) {
|
||||
return false
|
||||
}
|
||||
if !samePatchLevelVersion(v, base) {
|
||||
return false
|
||||
}
|
||||
if target != nil && !v.LessThan(target) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, v := range m.exclude {
|
||||
if v.Equal(version) {
|
||||
return false
|
||||
|
||||
for _, u := range releaseLineUpgraders {
|
||||
v := u.Version()
|
||||
if versionFilter(v) {
|
||||
path = append(path, v)
|
||||
}
|
||||
}
|
||||
if m.min != nil && version.LessThan(m.min) {
|
||||
return false
|
||||
|
||||
if target != nil {
|
||||
path = append(path, target)
|
||||
}
|
||||
if m.max != nil && version.GreaterThan(m.max) {
|
||||
return false
|
||||
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func getUpgraderByVersion(target *semver.Version) upgrader {
|
||||
for _, upgraders := range [][]breakingUpgrader{
|
||||
dailyUpgraders,
|
||||
mainUpgraders,
|
||||
} {
|
||||
|
||||
for _, u := range upgraders {
|
||||
if u.Version().Equal(target) {
|
||||
return u
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
return upgraderBase{}
|
||||
}
|
||||
|
||||
// todo: do we need to check at least 1.12 in cli?
|
||||
var anyVersion versionMatcher = &explicitVersionMatcher{}
|
||||
var atLeasVersion112 versionMatcher = &explicitVersionMatcher{min: semver.New(1, 12, 0, "1", "")}
|
||||
|
||||
type upgradeTask struct {
|
||||
Task task.Interface
|
||||
Current versionMatcher
|
||||
Target versionMatcher
|
||||
func samePatchLevelVersion(a, b *semver.Version) bool {
|
||||
return a.Major() == b.Major() && a.Minor() == b.Minor() && a.Patch() == b.Patch()
|
||||
}
|
||||
|
||||
func (t *upgradeTask) Match(current, target *semver.Version) bool {
|
||||
return t.Current.Match(current) && t.Target.Match(target)
|
||||
func sameMajorLevelVersion(a, b *semver.Version) bool {
|
||||
return a.Major() == b.Major()
|
||||
}
|
||||
|
||||
@@ -191,9 +191,11 @@ func CheckJWS(jws string, duration int64) (*CheckJWSResult, error) {
|
||||
|
||||
// Verify DID matches
|
||||
if resolutionResult.Document.ID != kid {
|
||||
return nil, fmt.Errorf("DID does not match")
|
||||
}
|
||||
|
||||
sid := resolutionResult.Document.ID + resolutionResult.Document.VerificationMethod[0].ID
|
||||
if sid != kid {
|
||||
return nil, fmt.Errorf("DID does not match: expected %s, got % s", sid, kid)
|
||||
}
|
||||
}
|
||||
// Get verification method
|
||||
if len(resolutionResult.Document.VerificationMethod) == 0 || resolutionResult.Document.VerificationMethod[0].PublicKeyJwk == nil {
|
||||
return nil, fmt.Errorf("invalid DID document: missing verification method")
|
||||
|
||||
@@ -20,7 +20,7 @@ require (
|
||||
bytetrade.io/web3os/bfl v0.0.0-00010101000000-000000000000
|
||||
github.com/Masterminds/semver/v3 v3.3.0
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
github.com/beclab/Olares/cli v0.0.0-20250612062319-688c4b401082
|
||||
github.com/beclab/Olares/cli v0.0.0-20250715142112-cd6c89f724b5
|
||||
github.com/containerd/containerd v1.7.27
|
||||
github.com/eball/zeroconf v0.2.1
|
||||
github.com/godbus/dbus/v5 v5.1.0
|
||||
@@ -90,6 +90,7 @@ require (
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/geoffgarside/ber v1.1.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
@@ -97,6 +98,9 @@ require (
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
@@ -111,14 +115,19 @@ require (
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/jaypipes/pcidb v1.0.1 // indirect
|
||||
github.com/jkeiser/iter v0.0.0-20200628201005-c8aa0ae784d1 // indirect
|
||||
github.com/jmoiron/sqlx v1.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lithammer/dedent v1.1.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/miekg/dns v1.1.55 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
@@ -137,6 +146,7 @@ require (
|
||||
github.com/opencontainers/runc v1.3.0 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.2.1 // indirect
|
||||
github.com/opencontainers/selinux v1.11.1 // indirect
|
||||
github.com/pkg/sftp v1.13.6 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
@@ -166,6 +176,8 @@ require (
|
||||
go.opentelemetry.io/otel/metric v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.33.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
@@ -183,6 +195,7 @@ require (
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.33.0 // indirect
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA=
|
||||
@@ -24,8 +26,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/beclab/Olares/cli v0.0.0-20250612062319-688c4b401082 h1:HgUvoDR1IUx3GpFcM91KHL69dIslDzV00MN5thX3vBg=
|
||||
github.com/beclab/Olares/cli v0.0.0-20250612062319-688c4b401082/go.mod h1:fJ/f0348rB+ssDIvsaPh0tPQmdFbtwY4R0qkfQuGXw0=
|
||||
github.com/beclab/Olares/cli v0.0.0-20250715142112-cd6c89f724b5 h1:1wWOAV6O3D4cFaGCB3I0YTFjzYYWPj8FjnIVCvmNVZc=
|
||||
github.com/beclab/Olares/cli v0.0.0-20250715142112-cd6c89f724b5/go.mod h1:fJ/f0348rB+ssDIvsaPh0tPQmdFbtwY4R0qkfQuGXw0=
|
||||
github.com/beclab/app-service v0.2.33 h1:fsv9sTL7guTdU8z8sO5KIxxd1N5K+Rp4zORRebs+wmI=
|
||||
github.com/beclab/app-service v0.2.33/go.mod h1:Gpp5e2XPU/nHufT7ZBsRMZrYxpFbI6R4AEiKine+RhI=
|
||||
github.com/beclab/bfl v0.3.36 h1:PgeSPGc+XoONiwFsKq9xX8rqcL4kVM1G/ut0lYYj/js=
|
||||
@@ -97,6 +99,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
|
||||
github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -115,6 +119,16 @@ github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
@@ -185,6 +199,8 @@ github.com/jaypipes/pcidb v1.0.1/go.mod h1:6xYUz/yYEyOkIkUt2t2J2folIuZ4Yg6uByCGF
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jkeiser/iter v0.0.0-20200628201005-c8aa0ae784d1 h1:smvLGU3obGU5kny71BtE/ibR0wIXRUiRFDmSn0Nxz1E=
|
||||
github.com/jkeiser/iter v0.0.0-20200628201005-c8aa0ae784d1/go.mod h1:fP/NdyhRVOv09PLRbVXrSqHhrfQypdZwgE2L4h2U5C8=
|
||||
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||
github.com/jochenvg/go-udev v0.0.0-20171110120927-d6b62d56d37b h1:dgF9Rx3oPIz2d816jKSjnShkJfmtYc/N/DxGDFv2CGk=
|
||||
github.com/jochenvg/go-udev v0.0.0-20171110120927-d6b62d56d37b/go.mod h1:IBDUGq30U56w969YNPomhMbRje1GrhUsCh7tHdwgLXA=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
@@ -200,14 +216,22 @@ github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYW
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/libp2p/go-netroute v0.2.2 h1:Dejd8cQ47Qx2kRABg6lPwknU7+nBnFRpko45/fFPuZ8=
|
||||
github.com/libp2p/go-netroute v0.2.2/go.mod h1:Rntq6jUAH0l9Gg17w5bFGhcC9a+vk4KNXs6s7IljKYE=
|
||||
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
|
||||
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/mackerelio/go-osstat v0.2.5 h1:+MqTbZUhoIt4m8qzkVoXUJg1EuifwlAJSk4Yl2GXh+o=
|
||||
@@ -221,6 +245,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
||||
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
@@ -282,6 +308,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
|
||||
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -351,6 +379,7 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
@@ -385,6 +414,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -397,6 +428,7 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPI
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -411,6 +443,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -422,6 +456,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -436,10 +471,14 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -447,11 +486,15 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
@@ -465,6 +508,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -516,6 +560,8 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
@@ -24,23 +24,28 @@ func init() {
|
||||
handlers.RunCommand(handlers.PostTerminusInit, install.New))))
|
||||
|
||||
cmd.Post("/uninstall", handlers.RequireSignature(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostTerminusUninstall, uninstall.New))))
|
||||
handlers.RequireOwner(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostTerminusUninstall, uninstall.New)))))
|
||||
|
||||
cmd.Post("/upgrade", handlers.RequireSignature(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.RequestOlaresUpgrade, upgrade.NewCreateUpgradeTarget))))
|
||||
handlers.RequireOwner(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.RequestOlaresUpgrade, upgrade.NewCreateUpgradeTarget)))))
|
||||
|
||||
cmd.Delete("/upgrade", handlers.RequireSignature(
|
||||
handlers.RunCommand(handlers.CancelOlaresUpgrade, upgrade.NewRemoveUpgradeTarget)))
|
||||
handlers.RequireOwner(
|
||||
handlers.RunCommand(handlers.CancelOlaresUpgrade, upgrade.NewRemoveUpgradeTarget))))
|
||||
|
||||
cmd.Post("/reboot", handlers.RequireSignature(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostReboot, reboot.New))))
|
||||
handlers.RequireOwner(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostReboot, reboot.New)))))
|
||||
|
||||
cmd.Post("/shutdown", handlers.RequireSignature(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostShutdown, shutdown.New))))
|
||||
handlers.RequireOwner(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostShutdown, shutdown.New)))))
|
||||
|
||||
cmd.Post("/connect-wifi", handlers.RequireSignature(
|
||||
handlers.WaitServerRunning(
|
||||
@@ -50,34 +55,34 @@ func init() {
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostChangeHost, changehost.New))))
|
||||
|
||||
cmd.Post("/umount-usb", handlers.RequireSignature(
|
||||
cmd.Post("/umount-usb", handlers.RequireLocal(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostUmountUsb, umountusb.New))))
|
||||
|
||||
cmd.Post("/umount-usb-incluster", handlers.RequireSignature(
|
||||
cmd.Post("/umount-usb-incluster", handlers.RequireLocal(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostUmountUsbInCluster, umountusb.New))))
|
||||
|
||||
cmd.Post("/collect-logs", handlers.RequireSignature(
|
||||
cmd.Post("/collect-logs", handlers.RequireLocal(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostCollectLogs, collectlogs.New))))
|
||||
|
||||
cmd.Post("/mount-samba", handlers.RequireSignature(
|
||||
cmd.Post("/mount-samba", handlers.RequireLocal(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostMountSambaDriver, mountsmb.New))))
|
||||
|
||||
cmd.Post("/umount-samba", handlers.RequireSignature(
|
||||
cmd.Post("/umount-samba", handlers.RequireLocal(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostUmountSmb, umountsmb.New))))
|
||||
|
||||
cmd.Post("/umount-samba-incluster", handlers.RequireSignature(
|
||||
cmd.Post("/umount-samba-incluster", handlers.RequireLocal(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostUmountSmbInCluster, umountsmb.New))))
|
||||
|
||||
cmdv2 := cmd.Group("v2")
|
||||
cmdv2.Post("/mount-samba", handlers.RequireSignature(
|
||||
cmdv2.Post("/mount-samba", handlers.RequireLocal(
|
||||
handlers.WaitServerRunning(
|
||||
handlers.RunCommand(handlers.PostMountSambaDriverV2, mountsmb.New))))
|
||||
|
||||
klog.Info("command handlers initialized")
|
||||
klog.V(8).Info("command handlers initialized")
|
||||
}
|
||||
|
||||
@@ -8,21 +8,21 @@ import (
|
||||
func init() {
|
||||
s := server.API
|
||||
containerd := s.App.Group("containerd")
|
||||
containerd.Get("/registries", handlers.RequireSignature(handlers.ListRegistries))
|
||||
containerd.Get("/registries", handlers.RequireLocal(handlers.ListRegistries))
|
||||
|
||||
registry := containerd.Group("registry")
|
||||
mirrors := registry.Group("mirrors")
|
||||
|
||||
mirrors.Get("/", handlers.RequireSignature(handlers.GetRegistryMirrors))
|
||||
mirrors.Get("/:registry", handlers.RequireSignature(handlers.GetRegistryMirror))
|
||||
mirrors.Put("/:registry", handlers.RequireSignature(handlers.UpdateRegistryMirror))
|
||||
mirrors.Delete("/:registry", handlers.RequireSignature(handlers.DeleteRegistryMirror))
|
||||
mirrors.Get("/", handlers.RequireLocal(handlers.GetRegistryMirrors))
|
||||
mirrors.Get("/:registry", handlers.RequireLocal(handlers.GetRegistryMirror))
|
||||
mirrors.Put("/:registry", handlers.RequireLocal(handlers.UpdateRegistryMirror))
|
||||
mirrors.Delete("/:registry", handlers.RequireLocal(handlers.DeleteRegistryMirror))
|
||||
|
||||
image := containerd.Group("images")
|
||||
|
||||
image.Get("/", handlers.RequireSignature(handlers.ListImages))
|
||||
image.Delete("/:image", handlers.RequireSignature(handlers.DeleteImage))
|
||||
image.Post("/prune", handlers.RequireSignature(handlers.PruneImages))
|
||||
image.Get("/", handlers.RequireLocal(handlers.ListImages))
|
||||
image.Delete("/:image", handlers.RequireLocal(handlers.DeleteImage))
|
||||
image.Post("/prune", handlers.RequireLocal(handlers.PruneImages))
|
||||
|
||||
klog.Info("containerd handlers initialized")
|
||||
klog.V(8).Info("containerd handlers initialized")
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/beclab/Olares/daemon/internel/client"
|
||||
"github.com/beclab/Olares/daemon/pkg/cluster/state"
|
||||
"github.com/beclab/Olares/daemon/pkg/commands"
|
||||
"github.com/beclab/Olares/daemon/pkg/utils"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
@@ -42,6 +43,33 @@ func (h *Handlers) RequireSignature(next func(ctx *fiber.Ctx) error) func(ctx *f
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handlers) RequireLocal(next func(ctx *fiber.Ctx) error) func(ctx *fiber.Ctx) error {
|
||||
return func(ctx *fiber.Ctx) error {
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handlers) RequireOwner(next func(ctx *fiber.Ctx) error) func(ctx *fiber.Ctx) error {
|
||||
return func(ctx *fiber.Ctx) error {
|
||||
c, ok := ctx.Context().UserValue(client.ClIENT_CONTEXT).(client.Client)
|
||||
if !ok {
|
||||
return h.ErrJSON(ctx, http.StatusForbidden, "client not found")
|
||||
}
|
||||
|
||||
// get owner from release file
|
||||
envOlaresID, err := utils.GetOlaresNameFromReleaseFile()
|
||||
if err != nil {
|
||||
return h.ErrJSON(ctx, http.StatusInternalServerError, "failed to get Olares ID from release file")
|
||||
}
|
||||
|
||||
if c.OlaresID() != envOlaresID {
|
||||
return h.ErrJSON(ctx, http.StatusForbidden, "not the owner of this Olares")
|
||||
}
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handlers) RunCommand(next func(ctx *fiber.Ctx, cmd commands.Interface) error,
|
||||
cmdNew func() commands.Interface) func(ctx *fiber.Ctx) error {
|
||||
|
||||
|
||||
@@ -8,18 +8,18 @@ import (
|
||||
func init() {
|
||||
s := server.API
|
||||
system := s.App.Group("system")
|
||||
system.Get("/status", handlers.RequireSignature(handlers.GetTerminusState))
|
||||
system.Get("/ifs", handlers.RequireSignature(handlers.GetNetIfs))
|
||||
system.Get("/hosts-file", handlers.RequireSignature(handlers.GetHostsfile))
|
||||
system.Post("/hosts-file", handlers.RequireSignature(handlers.PostHostsfile))
|
||||
system.Get("/mounted-usb", handlers.RequireSignature(handlers.GetMountedUsb))
|
||||
system.Get("/mounted-hdd", handlers.RequireSignature(handlers.GetMountedHdd))
|
||||
system.Get("/mounted-smb", handlers.RequireSignature(handlers.GetMountedSmb))
|
||||
system.Get("/mounted-path", handlers.RequireSignature(handlers.GetMountedPath))
|
||||
system.Get("/mounted-usb-incluster", handlers.RequireSignature(handlers.GetMountedUsbInCluster))
|
||||
system.Get("/mounted-hdd-incluster", handlers.RequireSignature(handlers.GetMountedHddInCluster))
|
||||
system.Get("/mounted-smb-incluster", handlers.RequireSignature(handlers.GetMountedSmbInCluster))
|
||||
system.Get("/mounted-path-incluster", handlers.RequireSignature(handlers.GetMountedPathInCluster))
|
||||
system.Get("/status", handlers.RequireLocal(handlers.GetTerminusState))
|
||||
system.Get("/ifs", handlers.RequireLocal(handlers.GetNetIfs))
|
||||
system.Get("/hosts-file", handlers.RequireLocal(handlers.GetHostsfile))
|
||||
system.Post("/hosts-file", handlers.RequireLocal(handlers.PostHostsfile))
|
||||
system.Get("/mounted-usb", handlers.RequireLocal(handlers.GetMountedUsb))
|
||||
system.Get("/mounted-hdd", handlers.RequireLocal(handlers.GetMountedHdd))
|
||||
system.Get("/mounted-smb", handlers.RequireLocal(handlers.GetMountedSmb))
|
||||
system.Get("/mounted-path", handlers.RequireLocal(handlers.GetMountedPath))
|
||||
system.Get("/mounted-usb-incluster", handlers.RequireLocal(handlers.GetMountedUsbInCluster))
|
||||
system.Get("/mounted-hdd-incluster", handlers.RequireLocal(handlers.GetMountedHddInCluster))
|
||||
system.Get("/mounted-smb-incluster", handlers.RequireLocal(handlers.GetMountedSmbInCluster))
|
||||
system.Get("/mounted-path-incluster", handlers.RequireLocal(handlers.GetMountedPathInCluster))
|
||||
|
||||
klog.Info("system handlers initialized")
|
||||
klog.V(8).Info("system handlers initialized")
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ import (
|
||||
"github.com/beclab/Olares/daemon/internel/apiserver/handlers"
|
||||
"github.com/beclab/Olares/daemon/internel/apiserver/server"
|
||||
"github.com/beclab/Olares/daemon/internel/ble"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
)
|
||||
|
||||
func NewServer(ctx context.Context, port int) *server.Server {
|
||||
@@ -20,8 +18,5 @@ func NewServer(ctx context.Context, port int) *server.Server {
|
||||
|
||||
s := server.API
|
||||
|
||||
s.App.Use(cors.New())
|
||||
s.App.Use(logger.New())
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
|
||||
"github.com/beclab/Olares/daemon/internel/ble"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
@@ -15,7 +17,13 @@ type Server struct {
|
||||
}
|
||||
|
||||
var API *Server = &Server{
|
||||
App: fiber.New(),
|
||||
App: func() *fiber.App {
|
||||
a := fiber.New()
|
||||
a.Use(cors.New())
|
||||
a.Use(logger.New())
|
||||
|
||||
return a
|
||||
}(),
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
|
||||
@@ -7,15 +7,28 @@ const (
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
OlaresID() string
|
||||
}
|
||||
|
||||
var _ Client = &termipass{}
|
||||
|
||||
type termipass struct {
|
||||
Client
|
||||
jws string
|
||||
jws string
|
||||
olaresID string
|
||||
}
|
||||
|
||||
// OlaresID implements Client.
|
||||
func (c *termipass) OlaresID() string {
|
||||
return c.olaresID
|
||||
}
|
||||
|
||||
func NewTermipassClient(ctx context.Context, jws string) (Client, error) {
|
||||
c := &termipass{jws: jws}
|
||||
err, olaresID := c.validateJWS(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, c.validateJWS(ctx)
|
||||
c.olaresID = olaresID
|
||||
return c, nil
|
||||
}
|
||||
|
||||
@@ -5,33 +5,20 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/beclab/Olares/daemon/pkg/utils"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func (c *termipass) validateJWS(_ context.Context) error {
|
||||
func (c *termipass) validateJWS(_ context.Context) (error, string) {
|
||||
if strings.TrimSpace(c.jws) == "" {
|
||||
klog.Error("jws is empty")
|
||||
return errors.New("invalid jws")
|
||||
return errors.New("invalid jws"), ""
|
||||
}
|
||||
|
||||
// if state.CurrentState.TerminusState == state.TerminusRunning {
|
||||
// client, err := utils.GetDynamicClient()
|
||||
// if err != nil {
|
||||
// klog.Error("get k8s client error, ", err)
|
||||
// return err
|
||||
// }
|
||||
|
||||
// jws, err := utils.GetAdminUserJws(ctx, client)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// if c.jws != jws {
|
||||
// return errors.New("invalid jws of admin user")
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO: validate jws on blockchain
|
||||
|
||||
return nil
|
||||
if ok, olaresID := utils.ValidateJWS(c.jws); ok {
|
||||
return nil, olaresID
|
||||
} else {
|
||||
klog.Error("jws validation failed")
|
||||
return errors.New("invalid jws"), ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +225,7 @@ func CheckCurrentStatus(ctx context.Context) error {
|
||||
|
||||
if !slices.ContainsFunc(ips, func(i *nets.NetInterface) bool { return i.IP == hostIp }) {
|
||||
// wrong host ip
|
||||
klog.Warningf("host ip %s not in internal ips, try to fix it", hostIp)
|
||||
if err = fix(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -233,6 +234,7 @@ func CheckCurrentStatus(ctx context.Context) error {
|
||||
if conflict, err := nets.ConflictDomainIpInHostsFile(hostname); err != nil {
|
||||
return err
|
||||
} else if conflict {
|
||||
klog.Warningf("domain %s conflict with internal ip, try to fix it", hostname)
|
||||
if err = fix(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -108,6 +108,21 @@ func GetInternalIpv4Addr(opts ...any) (internalAddrs []*NetInterface, err error)
|
||||
}
|
||||
|
||||
func GetHostIp() (addr string, err error) {
|
||||
addrs, err := LookupHostIps()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(addrs) == 0 {
|
||||
err = errors.New("host ip not found")
|
||||
return
|
||||
}
|
||||
|
||||
addr = addrs[0]
|
||||
return
|
||||
}
|
||||
|
||||
func LookupHostIps() (addrs []string, err error) {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
klog.Error("get hostname error, ", err)
|
||||
@@ -122,13 +137,20 @@ func GetHostIp() (addr string, err error) {
|
||||
|
||||
for _, ip := range ips {
|
||||
ipv4 := ip.To4()
|
||||
if ipv4 != nil {
|
||||
addr = ipv4.String()
|
||||
return
|
||||
if ipv4 != nil && ipv4.IsGlobalUnicast() {
|
||||
addr := ipv4.String()
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
}
|
||||
|
||||
err = errors.New("host ip not found")
|
||||
if len(addrs) == 0 {
|
||||
// lookup in hosts file
|
||||
if ip, e := GetHostIpFromHostsFile(hostname); e == nil && len(ip) > 0 {
|
||||
addrs = append(addrs, ip)
|
||||
} else {
|
||||
err = errors.New("host ip not found")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -7,16 +7,16 @@ import (
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func ValidateJWS(token string) bool {
|
||||
func ValidateJWS(token string) (bool, string) {
|
||||
checkJWS, err := jws.CheckJWS(token, 20*60*1000)
|
||||
if err != nil {
|
||||
klog.Errorf("failed to check JWS: %v", err)
|
||||
return false
|
||||
return false, ""
|
||||
}
|
||||
|
||||
if checkJWS == nil {
|
||||
klog.Error("JWS validation failed: JWS is nil")
|
||||
return false
|
||||
return false, ""
|
||||
}
|
||||
|
||||
// Convert to JSON with indentation
|
||||
@@ -26,5 +26,5 @@ func ValidateJWS(token string) bool {
|
||||
}
|
||||
|
||||
klog.Infof("JWS validation successful: %s", string(bytes))
|
||||
return true
|
||||
return true, checkJWS.OlaresID
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ import (
|
||||
sysv1 "bytetrade.io/web3os/app-service/api/sys.bytetrade.io/v1alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
RoleOwner = "owner"
|
||||
)
|
||||
|
||||
func GetKubeClient() (kubernetes.Interface, error) {
|
||||
config, err := ctrl.GetConfig()
|
||||
if err != nil {
|
||||
@@ -74,7 +78,7 @@ func IsTerminusInitialized(ctx context.Context, client dynamic.Interface) (initi
|
||||
continue
|
||||
}
|
||||
|
||||
if role == bflconst.RolePlatformAdmin {
|
||||
if role == RoleOwner {
|
||||
status, ok := u.GetAnnotations()[bflconst.UserTerminusWizardStatus]
|
||||
if !ok {
|
||||
initialized = false
|
||||
@@ -137,27 +141,44 @@ func IsIpChanged(ctx context.Context, installed bool) (bool, error) {
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
hostIp, err := nets.GetHostIp()
|
||||
hostIps, err := nets.LookupHostIps()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
masterIp, err := MasterNodeIp(installed)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, hostIp := range hostIps {
|
||||
if hostIp == ip.IP {
|
||||
|
||||
if !installed {
|
||||
// terminus not installed
|
||||
return false, nil
|
||||
}
|
||||
|
||||
kubeClient, err := GetKubeClient()
|
||||
if err != nil {
|
||||
klog.Error("get kube client error, ", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
_, nodeIp, nodeRole, err := GetThisNodeName(ctx, kubeClient)
|
||||
if err != nil {
|
||||
klog.Error("get this node name error, ", err)
|
||||
return false, nil
|
||||
|
||||
}
|
||||
|
||||
if nodeRole == "master" && nodeIp == ip.IP {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// FIXME:(BUG) worker node will not work with this check
|
||||
if nodeRole == "worker" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
klog.Info("get node ip, ", nodeIp, ", ", hostIp, ", ", ip.IP)
|
||||
|
||||
if hostIp == ip.IP {
|
||||
if masterIp == "" {
|
||||
// terminus not installed
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if masterIp == ip.IP {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
klog.Info("get master node ip, ", masterIp, ", ", hostIp, ", ", ip.IP)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +307,7 @@ func GetAdminUser(ctx context.Context, client dynamic.Interface) (*unstructured.
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return role == bflconst.RolePlatformAdmin
|
||||
return role == RoleOwner
|
||||
})
|
||||
if err != nil {
|
||||
klog.Error("list user error, ", err)
|
||||
@@ -401,7 +422,7 @@ func GetTerminusInitializedTime(ctx context.Context, client kubernetes.Interface
|
||||
return pointer.Int64(deploy.CreationTimestamp.Unix()), nil
|
||||
}
|
||||
|
||||
func GetThisNodeName(ctx context.Context, client kubernetes.Interface) (nodeName, nodeIp string, err error) {
|
||||
func GetThisNodeName(ctx context.Context, client kubernetes.Interface) (nodeName, nodeIp, nodeRole string, err error) {
|
||||
nodes, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
klog.Error("list nodes error, ", err)
|
||||
@@ -414,7 +435,7 @@ func GetThisNodeName(ctx context.Context, client kubernetes.Interface) (nodeName
|
||||
return
|
||||
}
|
||||
|
||||
ip, err := nets.GetHostIp()
|
||||
ips, err := nets.LookupHostIps()
|
||||
if err != nil {
|
||||
klog.Error("get host ip error, ", err)
|
||||
return
|
||||
@@ -427,14 +448,25 @@ func GetThisNodeName(ctx context.Context, client kubernetes.Interface) (nodeName
|
||||
case corev1.NodeHostName:
|
||||
foundHost = address.Address == hostname
|
||||
case corev1.NodeInternalIP:
|
||||
foundIp = address.Address == ip
|
||||
if foundIp {
|
||||
nodeIp = address.Address
|
||||
for _, ip := range ips {
|
||||
foundIp = address.Address == ip
|
||||
if foundIp {
|
||||
nodeIp = address.Address
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if foundHost && foundIp {
|
||||
nodeName = node.Name
|
||||
|
||||
if cp, ok := node.Labels["node-role.kubernetes.io/control-plane"]; ok && cp != "false" {
|
||||
nodeRole = "master"
|
||||
} else if master, ok := node.Labels["node-role.kubernetes.io/master"]; ok && master != "false" {
|
||||
nodeRole = "master"
|
||||
} else {
|
||||
nodeRole = "worker"
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
148
docs/.vitepress/config.mts
Normal file
148
docs/.vitepress/config.mts
Normal file
@@ -0,0 +1,148 @@
|
||||
import { defineConfig,UserConfig,DefaultTheme } from "vitepress";
|
||||
import { withMermaid } from "vitepress-plugin-mermaid";
|
||||
import { en } from "./en";
|
||||
import { zh } from "./zh";
|
||||
import _ from "lodash";
|
||||
//import defaultConfig from 'vitepress-versioning-plugin';
|
||||
|
||||
|
||||
|
||||
function defineVersionedConfig2(
|
||||
defaultConfig: UserConfig<DefaultTheme.Config>
|
||||
): UserConfig<DefaultTheme.Config> {
|
||||
let config = _.defaultsDeep(defaultConfig);
|
||||
|
||||
if( !process.env.BASE_URL || !process.env.VERSIONS || !process.env.LATEST_VERSION ) {
|
||||
return config;
|
||||
}
|
||||
|
||||
const versions = process.env.VERSIONS?.split(",");
|
||||
const latestVersion = process.env.LATEST_VERSION || versions![versions!.length - 1];
|
||||
console.log(versions, latestVersion);
|
||||
|
||||
for( const locale of Object.keys(config.locales) ) {
|
||||
let themeConfig = config.locales[locale]!.themeConfig!;
|
||||
|
||||
themeConfig?.nav?.push(
|
||||
{
|
||||
component: 'VersionSwitcher',
|
||||
// Optional props to pass to the component
|
||||
props: {
|
||||
versions,
|
||||
latestVersion,
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineVersionedConfig2(withMermaid({
|
||||
title: "Olares",
|
||||
description: "Let people own their data again",
|
||||
lang: "en",
|
||||
locales: {
|
||||
root: {
|
||||
label: "English",
|
||||
...en,
|
||||
},
|
||||
zh: {
|
||||
label: "简体中文",
|
||||
...zh,
|
||||
},
|
||||
},
|
||||
themeConfig: {
|
||||
search: {
|
||||
provider: "algolia",
|
||||
options: {
|
||||
appId: "DZ6H2FVQGO",
|
||||
apiKey: "e5257d88b605dc0e5b82b12854aea9a5",
|
||||
indexName: "olares",
|
||||
searchParameters: {
|
||||
queryLanguages: ["zh", "en"],
|
||||
facetFilters: ['version:' + (process.env.CURRENT_VERSION || "main")]
|
||||
},
|
||||
locales: {
|
||||
zh: {
|
||||
placeholder: "搜索文档",
|
||||
translations: {
|
||||
button: {
|
||||
buttonText: "搜索文档",
|
||||
buttonAriaLabel: "搜索文档",
|
||||
},
|
||||
modal: {
|
||||
searchBox: {
|
||||
resetButtonTitle: "清除查询条件",
|
||||
resetButtonAriaLabel: "清除查询条件",
|
||||
cancelButtonText: "取消",
|
||||
cancelButtonAriaLabel: "取消",
|
||||
},
|
||||
startScreen: {
|
||||
recentSearchesTitle: "搜索历史",
|
||||
noRecentSearchesText: "没有搜索历史",
|
||||
saveRecentSearchButtonTitle: "保存至搜索历史",
|
||||
removeRecentSearchButtonTitle: "从搜索历史中移除",
|
||||
favoriteSearchesTitle: "收藏",
|
||||
removeFavoriteSearchButtonTitle: "从收藏中移除",
|
||||
},
|
||||
errorScreen: {
|
||||
titleText: "无法获取结果",
|
||||
helpText: "你可能需要检查你的网络连接",
|
||||
},
|
||||
footer: {
|
||||
selectText: "选择",
|
||||
navigateText: "切换",
|
||||
closeText: "关闭",
|
||||
searchByText: "搜索提供者",
|
||||
},
|
||||
noResultsScreen: {
|
||||
noResultsText: "无法找到相关结果",
|
||||
suggestedQueryText: "你可以尝试查询",
|
||||
reportMissingResultsText: "你认为该查询应该有结果?",
|
||||
reportMissingResultsLinkText: "点击反馈",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
sitemap: {
|
||||
hostname: "https://docs.olares.com/",
|
||||
},
|
||||
lastUpdated: true,
|
||||
base: process.env.BASE_URL || "/",
|
||||
vite: {
|
||||
build: {
|
||||
minify: "terser",
|
||||
chunkSizeWarningLimit: Infinity,
|
||||
},
|
||||
define: {
|
||||
'process.env.VERSIONS': JSON.stringify(process.env.VERSIONS || JSON.stringify([])),
|
||||
'process.env.LANGUAGES': JSON.stringify(process.env.LANGUAGES || JSON.stringify([])),
|
||||
}
|
||||
},
|
||||
head: [
|
||||
[
|
||||
"meta",
|
||||
{
|
||||
name:"docsearch:version",
|
||||
content: process.env.CURRENT_VERSION || "main"
|
||||
},
|
||||
],
|
||||
// 引入 Material Design Icons
|
||||
[
|
||||
"link",
|
||||
{
|
||||
rel: "stylesheet",
|
||||
href: "https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,200,0..1,-50..200",
|
||||
},
|
||||
],
|
||||
],
|
||||
}));
|
||||
804
docs/.vitepress/en.ts
Normal file
804
docs/.vitepress/en.ts
Normal file
@@ -0,0 +1,804 @@
|
||||
import { defineConfig, type DefaultTheme } from "vitepress";
|
||||
|
||||
const side = {
|
||||
"/manual/": [
|
||||
{
|
||||
text: "What is Olares",
|
||||
link: "/manual/docs-home",
|
||||
items: [
|
||||
// A{ text: "Why Olares", link: "/manual/why-olares" },
|
||||
//{ text: "Feature comparison", link: "/manual/feature-overview" },
|
||||
{ text: "Olares architecture", link: "/manual/system-architecture" },
|
||||
{ text: "Compare Olares and NAS", link: "/manual/olares-vs-nas" },
|
||||
{
|
||||
text: "Help and support",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "FAQs", link: "/manual/help/faqs" },
|
||||
{
|
||||
text: "Request support",
|
||||
link: "/manual/help/request-technical-support",
|
||||
},
|
||||
//{
|
||||
// text: "Troubleshooting Guide",
|
||||
// link: "/manual/help/troubleshooting-guide",
|
||||
// },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Get started",
|
||||
collapsed: false,
|
||||
link: "/manual/get-started/",
|
||||
items: [
|
||||
// { text: "Quick start", link: "/manual/get-started/quick-start" },
|
||||
{
|
||||
text: "Create an Olares ID",
|
||||
link: "/manual/get-started/create-olares-id",
|
||||
},
|
||||
{
|
||||
text: "Install Olares",
|
||||
link: "/manual/get-started/install-olares",
|
||||
},
|
||||
{
|
||||
text: "Back up mnemonics",
|
||||
link: "/manual/larepass/back-up-mnemonics"
|
||||
},
|
||||
{
|
||||
text: "What's next",
|
||||
link: "/manual/get-started/next-steps",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "LarePass",
|
||||
link: "/manual/larepass/",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Manage accounts",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: "Create accounts", link:"/manual/larepass/create-account"},
|
||||
{text: "Back up mnemonics", link: "/manual/larepass/back-up-mnemonics"},
|
||||
{text: "Manage integrations", link:"/manual/larepass/integrations"},
|
||||
],
|
||||
},
|
||||
{text: "Manage VPN", link:"/manual/larepass/private-network"},
|
||||
{
|
||||
text: "Manage device",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: "Activate Olares", link:"/manual/larepass/activate-olares"},
|
||||
{text: "Manage Olares", link:"/manual/larepass/manage-olares"},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Manage files",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: "Common file operations", link:"/manual/larepass/manage-files"},
|
||||
{text: "Sync and share", link:"/manual/larepass/sync-share"}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "Manage passwords",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: "Autofill passwords", link: "/manual/larepass/autofill"},
|
||||
{text: "Generate 2FA codes", link: "/manual/larepass/two-factor-verification"},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Manage knowledge",
|
||||
link: "/manual/larepass/manage-knowledge",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Olares applications",
|
||||
collapsed: true,
|
||||
link: "/manual/olares/",
|
||||
items: [
|
||||
{ text: "Desktop", link: "/manual/olares/desktop", },
|
||||
{ text: "Market", link: "/manual/olares/market", },
|
||||
{
|
||||
text: "Files",
|
||||
collapsed: true,
|
||||
link: "/manual/olares/files/",
|
||||
items: [
|
||||
{
|
||||
text: "Basic file operations",
|
||||
link: "/manual/olares/files/add-edit-download",
|
||||
},
|
||||
{
|
||||
text: "Sync and share",
|
||||
link: "/manual/larepass/sync-share",
|
||||
},
|
||||
{
|
||||
text: "Mount SMB",
|
||||
link: "/manual/olares/files/mount-SMB",
|
||||
},
|
||||
{text: "Mount cloud storage",
|
||||
link: "/manual/olares/files/mount-cloud-storage",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Vault",
|
||||
collapsed: true,
|
||||
link: "/manual/olares/vault/",
|
||||
items: [
|
||||
{
|
||||
text: "Manage vault items",
|
||||
link: "/manual/olares/vault/vault-items",
|
||||
},
|
||||
{
|
||||
text: "Manage shared vault",
|
||||
link: "/manual/olares/vault/share-vault-items",
|
||||
},
|
||||
{
|
||||
text: "Autofill",
|
||||
link: "/manual/larepass/autofill",
|
||||
},
|
||||
{
|
||||
text: "Generate 2FA codes",
|
||||
link: "/manual/larepass/two-factor-verification",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Wise",
|
||||
collapsed: true,
|
||||
link: "/manual/olares/wise/",
|
||||
items: [
|
||||
{
|
||||
text: "Basic operations",
|
||||
link: "/manual/olares/wise/basics",
|
||||
},
|
||||
{
|
||||
text: "Get recommendation engine",
|
||||
link: "/manual/olares/wise/recommend",
|
||||
},
|
||||
{
|
||||
text: "Manage your feeds",
|
||||
link: "/manual/olares/wise/subscribe",
|
||||
},
|
||||
{
|
||||
text: "Organize your knowledge",
|
||||
link: "/manual/olares/wise/filter",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Control Hub",
|
||||
collapsed: true,
|
||||
link: "/manual/olares/controlhub/",
|
||||
items: [
|
||||
{
|
||||
text: "Navigate Control Hub",
|
||||
link: "/manual/olares/controlhub/navigate-control-hub",
|
||||
},
|
||||
{
|
||||
text: "Edit system resource",
|
||||
link: "/manual/olares/controlhub/edit-resource",
|
||||
},
|
||||
{
|
||||
text: "View container status",
|
||||
link: "/manual/olares/controlhub/view-container",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Settings",
|
||||
collapsed: true,
|
||||
link: "/manual/olares/settings/",
|
||||
items: [
|
||||
{
|
||||
text: "My Olares",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: "Account and device", link: "/manual/olares/settings/my-olares"},
|
||||
{text: "Update system", link: "/manual/olares/settings/update"},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Manage accounts",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Roles and permissions",
|
||||
link: "/manual/olares/settings/roles-permissions",
|
||||
},
|
||||
{
|
||||
text: "Create member accounts",
|
||||
link: "/manual/olares/settings/manage-team",
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Manage applications",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Manage app entrance",
|
||||
link: "/manual/olares/settings/manage-entrance",
|
||||
},
|
||||
{
|
||||
text: "Customize app domains",
|
||||
link: "/manual/olares/settings/custom-app-domain",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Manage integrations",
|
||||
link:"/manual/olares/settings/integrations",
|
||||
},
|
||||
{
|
||||
text: "Customize appearance",
|
||||
link:"/manual/olares/settings/language-appearance",
|
||||
},
|
||||
{text: "Manage VPN", link: "/manual/olares/settings/remote-access",},
|
||||
{
|
||||
text: "Configure network",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Change revere proxy",
|
||||
link: "/manual/olares/settings/change-frp",
|
||||
},
|
||||
{
|
||||
text: "Set up hosts file",
|
||||
link:"/manual/olares/settings/set-up-hosts",
|
||||
},
|
||||
],
|
||||
},
|
||||
{text: "Manage GPU", link: "/manual/olares/settings/gpu-resource",},
|
||||
{
|
||||
text: "Backup and restore",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: "Backup", link: "/manual/olares/settings/backup"},
|
||||
{text: "Restore", link: "/manual/olares/settings/restore"},
|
||||
],
|
||||
},
|
||||
{text: "Developer resources", link: "/manual/olares/settings/developer"},
|
||||
]
|
||||
},
|
||||
{text: "Dashboard", link: "/manual/olares/resources-usage"},
|
||||
{text: "Profile", link: "/manual/olares/profile"},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Best practices",
|
||||
link: "/manual/best-practices/",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Set up custom domain",
|
||||
link: "/manual/best-practices/set-custom-domain",
|
||||
},
|
||||
{
|
||||
text: "Manage knowledge with Wise",
|
||||
link: "/manual/best-practices/organize-content",
|
||||
},
|
||||
{
|
||||
text: "Install a multi-node Olares cluster",
|
||||
link: "/manual/best-practices/install-olares-multi-node",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Concepts",
|
||||
collapsed: true,
|
||||
link: "/manual/concepts/",
|
||||
items: [
|
||||
{ text: "Architecture", link: "/manual/concepts/architecture" },
|
||||
{ text:
|
||||
"Olares ID",
|
||||
link: "/manual/concepts/olares-id",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Decentralized ID",
|
||||
link: "/manual/concepts/did",
|
||||
},
|
||||
{
|
||||
text: "Blockchain Registry",
|
||||
link: "/manual/concepts/registry",
|
||||
},
|
||||
{
|
||||
text: "Verifiable Credential",
|
||||
link: "/manual/concepts/vc",
|
||||
},
|
||||
{
|
||||
text: "Autonomous Reputation",
|
||||
link: "/manual/concepts/reputation",
|
||||
},
|
||||
{
|
||||
text: "Self-Sovereign Network",
|
||||
link: "/manual/concepts/self-sovereign-network",
|
||||
},
|
||||
{
|
||||
text: "Identity Wallet",
|
||||
link: "/manual/concepts/wallet",
|
||||
},
|
||||
],
|
||||
},
|
||||
{ text: "Account", link: "/manual/concepts/account" },
|
||||
{ text: "Application", link: "/manual/concepts/application" },
|
||||
{ text: "Network", link: "/manual/concepts/network" },
|
||||
{ text: "Data", link: "/manual/concepts/data" },
|
||||
{ text: "Secrets", link: "/manual/concepts/secrets" },
|
||||
],
|
||||
},
|
||||
{ text: "Glossary", link: "/manual/glossary" },
|
||||
],
|
||||
"/space/": [
|
||||
{
|
||||
text: "Olares Space",
|
||||
link: "/space/",
|
||||
items: [
|
||||
{
|
||||
text: "Manage accounts",
|
||||
link: "/space/manage-accounts",
|
||||
},
|
||||
{
|
||||
text: "Host Olares",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Create Olares",
|
||||
link: "/space/create-olares",
|
||||
},
|
||||
{
|
||||
text: "Manage Olares",
|
||||
link: "/space/manage-olares",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Host domains",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Set up a custom domain",
|
||||
link: "/space/host-domain",
|
||||
},
|
||||
{
|
||||
text: "Manage a domain",
|
||||
link: "/space/manage-domain",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Back up and restore",
|
||||
link: "/space/backup-restore",
|
||||
},
|
||||
{ text: "Billing", link: "/space/billing" },
|
||||
],
|
||||
},
|
||||
],
|
||||
"/use-cases/": [
|
||||
{
|
||||
text: "Tutorials & use cases",
|
||||
link: "/use-cases/",
|
||||
items: [
|
||||
{
|
||||
text: "Stable Diffusion",
|
||||
link: "/use-cases/stable-diffusion",
|
||||
},
|
||||
{
|
||||
text: "ComfyUI",
|
||||
link: "/use-cases/comfyui",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Manage ComfyUI",
|
||||
link: "/use-cases/comfyui-launcher",
|
||||
},
|
||||
{
|
||||
text: "Use ComfyUI for Krita",
|
||||
link: "/use-cases/comfyui-for-krita",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "Ollama",
|
||||
link: "/use-cases/ollama",
|
||||
},
|
||||
{
|
||||
text: "Open WebUI",
|
||||
link: "/use-cases/openwebui",
|
||||
},
|
||||
{
|
||||
text: "Perplexica",
|
||||
link: "/use-cases/perplexica",
|
||||
},
|
||||
{
|
||||
text: "Dify",
|
||||
link: "/use-cases/dify",
|
||||
},
|
||||
{
|
||||
text: "Jellyfin",
|
||||
link: "/use-cases/stream-media",
|
||||
},
|
||||
{
|
||||
text: "Steam",
|
||||
link: "/use-cases/stream-game",
|
||||
},
|
||||
{
|
||||
text: "Redroid",
|
||||
link: "/use-cases/host-cloud-android",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"/developer/": [
|
||||
{
|
||||
text: "Installation deep-dives",
|
||||
link: "/developer/install/",
|
||||
items: [
|
||||
{
|
||||
text: "Installation architecture",
|
||||
link: "/developer/install/installation-overview",
|
||||
},
|
||||
{
|
||||
text: "Installation process",
|
||||
link: "/developer/install/installation-process",
|
||||
},
|
||||
{
|
||||
text: "Olares Home",
|
||||
link: "/developer/install/olares-home",
|
||||
},
|
||||
{
|
||||
text: "Environment variables",
|
||||
link: "/developer/install/environment-variables",
|
||||
},
|
||||
{
|
||||
text: "Olares CLI",
|
||||
link: "/developer/install/cli/olares-cli",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "gpu", link: "/developer/install/cli/gpu" },
|
||||
{ text: "osinfo", link: "/developer/install/cli/osinfo" },
|
||||
{ text: "node", link: "/developer/install/cli/node" },
|
||||
{
|
||||
text: "backups",
|
||||
link: "/developer/install/cli/backups",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: "download", link: "/developer/install/cli/backups-download"},
|
||||
{text: "region", link: "/developer/install/cli/backups-region"},
|
||||
{text: "backup", link: "/developer/install/cli/backups-backup"},
|
||||
{text: "restore", link: "/developer/install/cli/backups-restore"},
|
||||
{text: "snapshots", link: "/developer/install/cli/backups-snapshots"},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "change-ip",
|
||||
link: "/developer/install/cli/change-ip",
|
||||
},
|
||||
{
|
||||
text: "download",
|
||||
link: "/developer/install/cli/download",
|
||||
},
|
||||
{ text: "info", link: "/developer/install/cli/info" },
|
||||
{
|
||||
text: "install",
|
||||
link: "/developer/install/cli/install",
|
||||
},
|
||||
{
|
||||
text: "logs",
|
||||
link: "/developer/install/cli/logs",
|
||||
},
|
||||
{
|
||||
text: "precheck",
|
||||
link: "/developer/install/cli/precheck",
|
||||
},
|
||||
{
|
||||
text: "prepare",
|
||||
link: "/developer/install/cli/prepare",
|
||||
},
|
||||
{
|
||||
text: "release",
|
||||
link: "/developer/install/cli/release",
|
||||
},
|
||||
{
|
||||
text: "start",
|
||||
link: "/developer/install/cli/start",
|
||||
},
|
||||
{
|
||||
text: "stop",
|
||||
link: "/developer/install/cli/stop",
|
||||
},
|
||||
{
|
||||
text: "uninstall",
|
||||
link: "/developer/install/cli/uninstall",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Olares versioning",
|
||||
link: "/developer/install/versioning",
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// text: "Additional installations",
|
||||
// link: "/developer/install/additional-installations",
|
||||
// collapsed: true,
|
||||
// items: [
|
||||
// { text: "Linux (via Docker)", link: "/developer/install/linux-via-docker-compose"},
|
||||
// {
|
||||
// text: "macOS",
|
||||
// collapsed: true,
|
||||
// items: [
|
||||
// {
|
||||
// text: "Using the script (recommended)",
|
||||
// link: "/developer/install/mac",
|
||||
// },
|
||||
// {
|
||||
// text: "Using Docker image",
|
||||
// link: "/developer/install/mac-via-docker-image",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// text: "Windows (WSL 2)",
|
||||
// collapsed: true,
|
||||
// items: [
|
||||
// {
|
||||
// text: "Using the script (recommended)",
|
||||
// link: "/developer/install/windows",
|
||||
// },
|
||||
// {
|
||||
// text: "Using Docker image",
|
||||
// link: "/developer/install/windows-via-docker-image",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// { text: "PVE", link: "/developer/install/pve" },
|
||||
// { text: "LXC", link: "/developer/install/lxc" },
|
||||
// { text: "Raspberry Pi", link: "/developer/install/raspberry-pi" },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
text: "Develop Olares app",
|
||||
link: "/developer/develop/",
|
||||
items: [
|
||||
{
|
||||
text: "Tutorial",
|
||||
collapsed: true,
|
||||
link: "/developer/develop/tutorial/",
|
||||
items: [
|
||||
{
|
||||
text: "Learn Studio",
|
||||
link: "/developer/develop/tutorial/studio",
|
||||
},
|
||||
{
|
||||
text: "Create your first app",
|
||||
collapsed: true,
|
||||
link: "/developer/develop/tutorial/note/",
|
||||
items: [
|
||||
{
|
||||
text: "1. Create app",
|
||||
link: "/developer/develop/tutorial/note/create",
|
||||
},
|
||||
{
|
||||
text: "2. Develop backend",
|
||||
link: "/developer/develop/tutorial/note/backend",
|
||||
},
|
||||
{
|
||||
text: "3. Develop frontend",
|
||||
link: "/developer/develop/tutorial/note/frontend",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Application package",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Application chart",
|
||||
link: "/developer/develop/package/chart",
|
||||
},
|
||||
{
|
||||
text: "OlaresManifest",
|
||||
link: "/developer/develop/package/manifest",
|
||||
},
|
||||
{
|
||||
text: "Recommendation",
|
||||
link: "/developer/develop/package/recommend",
|
||||
},
|
||||
{
|
||||
text: "Helm extension",
|
||||
link: "/developer/develop/package/extension",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Advanced",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "terminus-info",
|
||||
link: "/developer/develop/advanced/terminus-info",
|
||||
},
|
||||
{
|
||||
text: "Service provider",
|
||||
link: "/developer/develop/advanced/provider",
|
||||
},
|
||||
{
|
||||
text: "AI",
|
||||
link: "/developer/develop/advanced/ai",
|
||||
},
|
||||
{ text: "Cookie", link: "/developer/develop/advanced/cookie" },
|
||||
{ text: "Database", link: "/developer/develop/advanced/database" },
|
||||
{
|
||||
text: "Account",
|
||||
link: "/developer/develop/advanced/account",
|
||||
},
|
||||
{
|
||||
text: "Market",
|
||||
link: "/developer/develop/advanced/market",
|
||||
},
|
||||
{
|
||||
text: "Websocket",
|
||||
link: "/developer/develop/advanced/websocket",
|
||||
},
|
||||
{
|
||||
text: "File upload",
|
||||
link: "/developer/develop/advanced/file-upload",
|
||||
},
|
||||
{
|
||||
text: "Secret",
|
||||
link: "/developer/develop/advanced/secret",
|
||||
},
|
||||
{
|
||||
text: "Kubesphere",
|
||||
link: "/developer/develop/advanced/kubesphere",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
text: "Submit application",
|
||||
collapsed: true,
|
||||
link: "/developer/develop/submit/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Contribute to Olares",
|
||||
items: [
|
||||
{
|
||||
text: "Develop system app",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Overview",
|
||||
link: "/developer/contribute/system-app/overview",
|
||||
},
|
||||
{
|
||||
text: "Configure deployment",
|
||||
link: "/developer/contribute/system-app/deployment",
|
||||
},
|
||||
{
|
||||
text: "Configure permissions",
|
||||
link: "/developer/contribute/system-app/olares-manifest",
|
||||
},
|
||||
{
|
||||
text: "Install",
|
||||
link: "/developer/contribute/system-app/install",
|
||||
},
|
||||
{
|
||||
text: "Other",
|
||||
link: "/developer/contribute/system-app/other",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Develop protocols",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Contract",
|
||||
link: "/developer/contribute/olares-id/contract/contract",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Architecture",
|
||||
link: "/developer/contribute/olares-id/contract/architecture",
|
||||
},
|
||||
{
|
||||
text: "DID",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Design",
|
||||
link: "/developer/contribute/olares-id/contract/did/design",
|
||||
},
|
||||
{
|
||||
text: "Official Taggers",
|
||||
link: "/developer/contribute/olares-id/contract/did/official-taggers",
|
||||
},
|
||||
{
|
||||
text: "Release History",
|
||||
link: "/developer/contribute/olares-id/contract/did/release-history",
|
||||
},
|
||||
{
|
||||
text: "FAQ",
|
||||
link: "/developer/contribute/olares-id/contract/did/faq",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Reputation",
|
||||
link: "/developer/contribute/olares-id/contract/contract-reputation",
|
||||
},
|
||||
{
|
||||
text: "Manage",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Contract",
|
||||
link: "/developer/contribute/olares-id/contract/manage/contract",
|
||||
},
|
||||
{
|
||||
text: "SDK",
|
||||
link: "/developer/contribute/olares-id/contract/manage/sdk",
|
||||
},
|
||||
{
|
||||
text: "Environment",
|
||||
link: "/developer/contribute/olares-id/contract/manage/environment",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Verifiable Credential",
|
||||
link: "/developer/contribute/olares-id/verifiable-credential/overview",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Issuer",
|
||||
link: "/developer/contribute/olares-id/verifiable-credential/issuer",
|
||||
},
|
||||
{
|
||||
text: "Verifer",
|
||||
link: "/developer/contribute/olares-id/verifiable-credential/verifer",
|
||||
},
|
||||
{
|
||||
text: "Olares",
|
||||
link: "/developer/contribute/olares-id/verifiable-credential/olares",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const en = defineConfig({
|
||||
lang: "en",
|
||||
themeConfig: {
|
||||
//logo: "/icon.png",
|
||||
socialLinks: [{ icon: "github", link: "https://github.com/beclab/olares" }],
|
||||
|
||||
nav: [
|
||||
{ text: "Olares", link: "/manual/docs-home" },
|
||||
{ text: "Olares Space", link: "/space/" },
|
||||
{ text: "Use Cases", link: "/use-cases/" },
|
||||
{ text: "Developer Guide", link: "/developer/install/" },
|
||||
],
|
||||
|
||||
sidebar: side,
|
||||
},
|
||||
});
|
||||
108
docs/.vitepress/theme/components/FilterableList.vue
Normal file
108
docs/.vitepress/theme/components/FilterableList.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div class="filterable-list">
|
||||
<!-- Tag filters -->
|
||||
<div class="filters">
|
||||
<label
|
||||
v-for="(tag, index) in uniqueTags"
|
||||
:key="index"
|
||||
class="filter-label"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
:value="tag"
|
||||
v-model="selectedTags"
|
||||
/>
|
||||
{{ tag }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- List of items -->
|
||||
<ul>
|
||||
<li v-for="(item, index) in filteredItems" :key="index">
|
||||
<a
|
||||
:href="item.link"
|
||||
:target="isExternalLink(item.link) ? '_blank' : '_self'"
|
||||
:rel="isExternalLink(item.link) ? 'noopener noreferrer' : ''"
|
||||
>
|
||||
{{ item.title }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FilterableList',
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
// Example structure of items:
|
||||
// [
|
||||
// { title: 'Guide 1', link: '/guide1.md', tags: ['tag1', 'tag2'] },
|
||||
// { title: 'Guide 2', link: '/guide2.md', tags: ['tag2', 'tag3'] },
|
||||
// ...
|
||||
// ]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTags: [] // Stores the currently selected tags for filtering
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// Compute the unique tags from the items
|
||||
uniqueTags() {
|
||||
const allTags = this.items.flatMap(item => item.tags)
|
||||
return [...new Set(allTags)]
|
||||
},
|
||||
// Filtered list based on selected tags
|
||||
filteredItems() {
|
||||
if (this.selectedTags.length === 0) {
|
||||
return this.items // No filtering if no tags are selected
|
||||
}
|
||||
return this.items.filter(item =>
|
||||
item.tags.some(tag => this.selectedTags.includes(tag))
|
||||
)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// Check if the link is external by looking for 'http' or 'https'
|
||||
isExternalLink(link) {
|
||||
return /^(http|https):\/\//.test(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Simple styling for the filterable list */
|
||||
.filterable-list {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.filters {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul li {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
ul li a {
|
||||
text-decoration: none;
|
||||
color: var(--vp-c-link);
|
||||
}
|
||||
|
||||
</style>
|
||||
90
docs/.vitepress/theme/components/LaunchCard.vue
Normal file
90
docs/.vitepress/theme/components/LaunchCard.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="launch-card">
|
||||
<div class="card-content">
|
||||
<h2>
|
||||
{{ title }}
|
||||
<a :href="'#' + slugify(title)" aria-hidden="true" class="anchor-link">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</h2>
|
||||
<p>{{ description }}</p>
|
||||
<ul>
|
||||
<li v-for="(link, index) in links" :key="index">
|
||||
<a :href="link.href">{{ link.text }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="portal-button" :id="'btn-' + slugify(title)" @click="navigate">
|
||||
{{ buttonText }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
title: String,
|
||||
description: String,
|
||||
links: Array,
|
||||
buttonText: String,
|
||||
buttonLink: String
|
||||
},
|
||||
methods: {
|
||||
slugify(text) {
|
||||
return text.toLowerCase().replace(/\s+/g, '-');
|
||||
},
|
||||
navigate() {
|
||||
window.location.href = this.buttonLink;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.launch-card-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.launch-card {
|
||||
flex: 1 1 calc(33.333% - 1rem);
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.card-content h2 {
|
||||
border: none;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.anchor-link {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
h2:hover .anchor-link {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.portal-button {
|
||||
background-color: #ec6464;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.portal-button:hover {
|
||||
background-color: #ec5a68;
|
||||
}
|
||||
</style>
|
||||
24
docs/.vitepress/theme/components/Layout.vue
Normal file
24
docs/.vitepress/theme/components/Layout.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<!-- docs/.vitepress/theme/Layout.vue -->
|
||||
<script setup lang="ts">
|
||||
import DefaultTheme from "vitepress/theme";
|
||||
import { useData } from "vitepress";
|
||||
import { watchEffect } from "vue";
|
||||
|
||||
const { lang } = useData();
|
||||
watchEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
document.cookie = `nf_lang=${lang.value}; expires=Mon, 1 Jan 2030 00:00:00 UTC; path=/`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DefaultTheme.Layout />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.medium-zoom-overlay,
|
||||
.medium-zoom-image {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
</style>
|
||||
97
docs/.vitepress/theme/components/OStabs.vue
Normal file
97
docs/.vitepress/theme/components/OStabs.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="os-tabs">
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
:key="tab.id"
|
||||
:class="{ active: currentTab === tab.id }"
|
||||
@click="handleTabClick(tab)"
|
||||
>
|
||||
{{ tab.label }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
defaultTab: {
|
||||
type: String,
|
||||
default: "linux", // Default tab if no match is found
|
||||
},
|
||||
tabs: {
|
||||
type: Array,
|
||||
required: true,
|
||||
// Expected format:
|
||||
// [
|
||||
// { id: "linux", label: "Linux", href: "/docs/linux" },
|
||||
// { id: "macos", label: "Mac", href: "/docs/macos" },
|
||||
// { id: "windows", label: "Windows", href: "/docs/windows" }
|
||||
// ]
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentTab: this.defaultTab, // Active tab starts with the defaultTab
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.setCurrentTabBasedOnURL(); // Set the active tab based on the current URL
|
||||
},
|
||||
methods: {
|
||||
handleTabClick(tab) {
|
||||
if (tab.href) {
|
||||
// Redirect to the tab's URL (full page reload)
|
||||
window.location.assign(tab.href);
|
||||
} else {
|
||||
// Otherwise, just switch the active tab locally
|
||||
this.currentTab = tab.id;
|
||||
}
|
||||
},
|
||||
setCurrentTabBasedOnURL() {
|
||||
// Get the current URL path
|
||||
const currentPath = window.location.pathname;
|
||||
|
||||
// Match the current path with the `href` of the tabs
|
||||
const matchingTab = this.tabs.find((tab) => tab.href === currentPath);
|
||||
|
||||
if (matchingTab) {
|
||||
this.currentTab = matchingTab.id; // Set the matching tab as active
|
||||
} else {
|
||||
// Fallback to the default tab if no match is found
|
||||
this.currentTab = this.defaultTab;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.os-tabs {
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.os-tabs button {
|
||||
flex: 1;
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: var(--vp-c-bg);
|
||||
color: var(--vp-c-text-primary);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: background-color 0.3s, color 0.3s, border-bottom 0.3s;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
.os-tabs button.active {
|
||||
background-color: var(--vp-c-surface);
|
||||
color: var(--vp-c-brand);
|
||||
border-bottom-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.os-tabs button:hover {
|
||||
background-color: var(--vp-c-surface);
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
</style>
|
||||
195
docs/.vitepress/theme/components/VersionSwitcher.vue
Normal file
195
docs/.vitepress/theme/components/VersionSwitcher.vue
Normal file
@@ -0,0 +1,195 @@
|
||||
<script setup lang="ts">
|
||||
import { useData, useRouter,inBrowser } from "vitepress"
|
||||
import { computed, ref } from 'vue'
|
||||
import VPMenuLink from 'vitepress/dist/client/theme-default/components/VPMenuLink.vue'
|
||||
import VPFlyout from 'vitepress/dist/client/theme-default/components/VPFlyout.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
versions: string[]
|
||||
latestVersion: string
|
||||
}>();
|
||||
|
||||
const router = useRouter();
|
||||
const { site } = useData();
|
||||
|
||||
const localUrl = computed(() => {
|
||||
let url = "/";
|
||||
if( inBrowser ) {
|
||||
url = window.location.href.split('/').slice(0,3).join('/');
|
||||
if( !url.endsWith('/') ) {
|
||||
url = url + '/';
|
||||
}
|
||||
}
|
||||
|
||||
//console.log('localUrl', url);
|
||||
return url;
|
||||
});
|
||||
|
||||
const currentVersion = computed(() => {
|
||||
let version = props.latestVersion;
|
||||
|
||||
for (const v of props.versions) {
|
||||
const u = `/${v}/`;
|
||||
// console.log('u', u);
|
||||
// console.log('router.route.path', router.route.path);
|
||||
if (router.route.path.startsWith(u)) {
|
||||
//console.log('match version', v);
|
||||
version = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return version;
|
||||
});
|
||||
|
||||
const customLink = (path) => path.replace(site.value.base || '', '');
|
||||
|
||||
const isOpen = ref(false);
|
||||
const toggle = () => {
|
||||
isOpen.value = !isOpen.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPFlyout class="VPVersionSwitcher" icon="vpi-versioning" :button="currentVersion"
|
||||
:label="'Switch Version'">
|
||||
<div class="items">
|
||||
<!-- <VPMenuLink v-if="currentVersion != latestVersion" :item="{
|
||||
text: latestVersion,
|
||||
link: `/`,
|
||||
}" /> -->
|
||||
<template v-for="version in versions" :key="version">
|
||||
<!-- <VPMenuLink v-if="currentVersion != version" :item="{
|
||||
text: version,
|
||||
link: `${localUrl}${version}/`,
|
||||
target: '_blank',
|
||||
rel: 'a'
|
||||
}" /> -->
|
||||
<a v-if="currentVersion != version" :href="`${localUrl}${version}/`" target="_blank">{{ version }}</a>
|
||||
</template>
|
||||
</div>
|
||||
</VPFlyout>
|
||||
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.vpi-versioning.option-icon {
|
||||
margin-right: 2px !important;
|
||||
}
|
||||
|
||||
.vpi-versioning {
|
||||
--icon: url("data:image/svg+xml;charset=utf-8;base64,PHN2ZyB3aWR0aD0iNjRweCIgaGVpZ2h0PSI2NHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHN0cm9rZS13aWR0aD0iMi4yIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNvbG9yPSIjMDAwMDAwIj48cGF0aCBkPSJNMTcgN0MxOC4xMDQ2IDcgMTkgNi4xMDQ1NyAxOSA1QzE5IDMuODk1NDMgMTguMTA0NiAzIDE3IDNDMTUuODk1NCAzIDE1IDMuODk1NDMgMTUgNUMxNSA2LjEwNDU3IDE1Ljg5NTQgNyAxNyA3WiIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjIuMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48L3BhdGg+PHBhdGggZD0iTTcgN0M4LjEwNDU3IDcgOSA2LjEwNDU3IDkgNUM5IDMuODk1NDMgOC4xMDQ1NyAzIDcgM0M1Ljg5NTQzIDMgNSAzLjg5NTQzIDUgNUM1IDYuMTA0NTcgNS44OTU0MyA3IDcgN1oiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIyLjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PC9wYXRoPjxwYXRoIGQ9Ik03IDIxQzguMTA0NTcgMjEgOSAyMC4xMDQ2IDkgMTlDOSAxNy44OTU0IDguMTA0NTcgMTcgNyAxN0M1Ljg5NTQzIDE3IDUgMTcuODk1NCA1IDE5QzUgMjAuMTA0NiA1Ljg5NTQzIDIxIDcgMjFaIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMi4yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjwvcGF0aD48cGF0aCBkPSJNNyA3VjE3IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMi4yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjwvcGF0aD48cGF0aCBkPSJNMTcgN1Y4QzE3IDEwLjUgMTUgMTEgMTUgMTFMOSAxM0M5IDEzIDcgMTMuNSA3IDE2VjE3IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMi4yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjwvcGF0aD48L3N2Zz4=")
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.VPVersionSwitcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.icon {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding: 0 24px 0 12px;
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.VPScreenVersionSwitcher {
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
height: 48px;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.5s;
|
||||
}
|
||||
|
||||
.VPVersionSwitcher a {
|
||||
display: block;
|
||||
border-radius: 6px;
|
||||
padding: 0 12px;
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-1);
|
||||
white-space: nowrap;
|
||||
transition:
|
||||
background-color 0.25s,
|
||||
color 0.25s;
|
||||
}
|
||||
|
||||
.VPVersionSwitcher a:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
background-color: var(--vp-c-default-soft);
|
||||
}
|
||||
|
||||
.VPVersionSwitcher a.active {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
|
||||
.VPScreenVersionSwitcher .items {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.VPScreenVersionSwitcher.open .items {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.VPScreenVersionSwitcher.open {
|
||||
padding-bottom: 10px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.VPScreenVersionSwitcher.open .button {
|
||||
padding-bottom: 6px;
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.VPScreenVersionSwitcher.open .button-icon {
|
||||
/*rtl:ignore*/
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.VPScreenVersionSwitcher button .icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 4px 11px 0;
|
||||
width: 100%;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-text-1);
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.button-icon {
|
||||
transition: transform 0.25s;
|
||||
}
|
||||
|
||||
.group:first-child {
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.group+.group,
|
||||
.group+.item {
|
||||
padding-top: 4px;
|
||||
}
|
||||
</style>
|
||||
122
docs/.vitepress/theme/components/tabs.vue
Normal file
122
docs/.vitepress/theme/components/tabs.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div class="tabs-container">
|
||||
<slot></slot>
|
||||
<div class="tabs">
|
||||
<button
|
||||
v-for="(tab, index) in tabLabels"
|
||||
:key="tab"
|
||||
@click="clickHandler(tab, index)"
|
||||
:class="{ active: activeTab === tab }"
|
||||
>
|
||||
<div class="tabs-item-wrapper">
|
||||
<img
|
||||
:src="iconFilter(index)"
|
||||
class="tabs-img-wrapper"
|
||||
alt=""
|
||||
v-if="iconFilter(index)"
|
||||
/>
|
||||
<span> {{ tab }}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div v-for="tab in tabLabels" :key="tab" v-show="activeTab === tab">
|
||||
<div v-if="activeTab === tab" class="tab-item">
|
||||
<slot :name="tabSlots[tab]"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
isDark: {
|
||||
type: Boolean,
|
||||
},
|
||||
icons: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: null,
|
||||
tabLabels: [],
|
||||
tabSlots: {},
|
||||
randomKey: Math.random(),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
clickHandler(tab, index) {
|
||||
this.activeTab = tab;
|
||||
this.$emit("tab-changed", tab, index);
|
||||
},
|
||||
iconFilter(index) {
|
||||
if (this.icons[index]) {
|
||||
return this.isDark
|
||||
? `/images/manual/icons/${this.icons[index]}-dark.svg`
|
||||
: `/images/manual/icons/${this.icons[index]}.svg`;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// Map slot names to display labels
|
||||
const slots = Object.keys(this.$slots).filter((slot) => slot !== "default");
|
||||
this.tabSlots = slots.reduce((map, slot) => {
|
||||
const label = slot.replace(/-/g, " "); // Replace hyphens with spaces
|
||||
map[label] = slot;
|
||||
return map;
|
||||
}, {});
|
||||
console.log("aa", this.tabSlots);
|
||||
|
||||
this.tabLabels = Object.keys(this.tabSlots);
|
||||
this.activeTab = this.tabLabels[0];
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.tabs {
|
||||
display: flex;
|
||||
background-color: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.tabs button {
|
||||
flex: 1;
|
||||
padding: 0.5rem 1rem;
|
||||
border: none;
|
||||
background-color: var(--vp-c-bg);
|
||||
color: var(--vp-c-text-primary);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
.tabs button.active {
|
||||
background-color: var(--vp-c-surface);
|
||||
color: var(--vp-c-brand);
|
||||
border-bottom-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.tabs button:hover {
|
||||
background-color: var(--vp-c-surface);
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
div[style] {
|
||||
color: var(--vp-c-text-primary);
|
||||
}
|
||||
.tabs-img-wrapper {
|
||||
width: 12px;
|
||||
pointer-events: none;
|
||||
}
|
||||
.tabs-item-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.tabs-container .tab-item {
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
131
docs/.vitepress/theme/index.ts
Normal file
131
docs/.vitepress/theme/index.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
// docs/.vitepress/theme/index.ts
|
||||
import DefaultTheme from "vitepress/theme";
|
||||
import "./styles/custom.css";
|
||||
import "./styles/index.css";
|
||||
import { inBrowser, useRoute, useRouter, useData } from "vitepress";
|
||||
import Layout from "./components/Layout.vue";
|
||||
import { App } from "vue";
|
||||
import Tabs from "./components/tabs.vue";
|
||||
import LaunchCard from "./components/LaunchCard.vue";
|
||||
import FilterableList from "./components/FilterableList.vue";
|
||||
import { onMounted, watch, nextTick, onBeforeMount,computed } from "vue";
|
||||
import mediumZoom from "medium-zoom";
|
||||
import OSTabs from "./components/OStabs.vue";
|
||||
import VersionSwitcher from "./components/VersionSwitcher.vue";
|
||||
import _ from "lodash";
|
||||
|
||||
const LANGUAGE_LOCAL_KEY = "language";
|
||||
let isMenuChange = false;
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
Layout,
|
||||
enhanceApp({ app }: { app: App }) {
|
||||
app.component("Tabs", Tabs);
|
||||
app.component("LaunchCard", LaunchCard);
|
||||
app.component("FilterableList", FilterableList);
|
||||
app.component("OSTabs", OSTabs);
|
||||
app.component("VersionSwitcher", VersionSwitcher);
|
||||
},
|
||||
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { lang } = useData();
|
||||
|
||||
const routerRedirect = () => {
|
||||
let localLanguage = localStorage.getItem(LANGUAGE_LOCAL_KEY) || 'en';
|
||||
|
||||
const versions = process.env.VERSIONS!.split(",") ||[];
|
||||
versions.push('default');
|
||||
|
||||
const languages = process.env.LANGUAGES!.split(",") || [];
|
||||
languages.push('en');
|
||||
console.log(versions, languages,localLanguage)
|
||||
|
||||
if(!languages?.includes(localLanguage) ){
|
||||
localLanguage = 'en';
|
||||
}
|
||||
|
||||
|
||||
const currentPath = router.route.path;
|
||||
|
||||
console.log('router.route.path', router.route.path);
|
||||
for( const l of languages ) {
|
||||
let localLanguagePath = (l === 'en' ? '' : `/${l}`);
|
||||
for (const v of versions) {
|
||||
let localVersionPath = (v === 'default' ? '' : `/${v}`);
|
||||
const u = `${localVersionPath}${localLanguagePath}`;
|
||||
console.log('checkPrefix', u);
|
||||
if (currentPath.startsWith(u)) {
|
||||
console.log('find localLanguage', localLanguage, l);
|
||||
if( l !== localLanguage ) {
|
||||
let targetLanguagePath = (localLanguage === 'en' ? '' : `/${localLanguage}`);
|
||||
const nextUrl = `${localVersionPath}${targetLanguagePath}${route.path.replace(u, '')}`;
|
||||
router.go(nextUrl);
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const initZoom = () => {
|
||||
mediumZoom(".main img", { background: "var(--vp-c-bg)" });
|
||||
};
|
||||
|
||||
const toggleMenuStatus = () => {
|
||||
const menuDom = document.querySelector(".menu .VPMenu");
|
||||
menuDom?.addEventListener("click", (e) => {
|
||||
const target = e.target as Element;
|
||||
const isLink = target.closest(".VPMenuLink");
|
||||
if (isLink) {
|
||||
isMenuChange = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (inBrowser) {
|
||||
routerRedirect();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
toggleMenuStatus();
|
||||
initZoom();
|
||||
|
||||
// document
|
||||
// .querySelector(".wrapper .container a.title")
|
||||
// ?.setAttribute("href", "https://www.olares.com/");
|
||||
|
||||
// document
|
||||
// .querySelector(".wrapper .container a.title")
|
||||
// ?.setAttribute("target", "_blank");
|
||||
});
|
||||
|
||||
watch(
|
||||
() => lang.value,
|
||||
(newValue) => {
|
||||
localStorage.setItem(LANGUAGE_LOCAL_KEY, newValue);
|
||||
isMenuChange = false;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
initZoom();
|
||||
|
||||
// document
|
||||
// .querySelector(".wrapper .container a.title")
|
||||
// ?.setAttribute("href", "https://www.olares.com/");
|
||||
|
||||
// document
|
||||
// .querySelector(".wrapper .container a.title")
|
||||
// ?.setAttribute("target", "_blank");
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
64
docs/.vitepress/theme/styles/cta-container.css
Normal file
64
docs/.vitepress/theme/styles/cta-container.css
Normal file
@@ -0,0 +1,64 @@
|
||||
.cta-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.cta-link {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
flex: 1 1 calc(33.333% - 1rem);
|
||||
padding: 1rem;
|
||||
text-decoration: none;
|
||||
border-radius: 8px;
|
||||
transition: box-shadow 0.3s ease, transform 0.3s ease;
|
||||
color: #333;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cta-link:hover {
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.cta-title {
|
||||
font-size: 20px;
|
||||
line-height: 1.4;
|
||||
letter-spacing: -0.02em;
|
||||
margin-bottom: 0.75em;
|
||||
color: #ec5b68;
|
||||
}
|
||||
|
||||
.cta-description {
|
||||
margin-bottom: 0;
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.cta-link {
|
||||
flex: 1 1 calc(50% - 1rem);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.cta-link {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
.cta-link {
|
||||
background-color: #222;
|
||||
color: #f5f5f5;
|
||||
}
|
||||
|
||||
.cta-title {
|
||||
color: #ff6f61;
|
||||
}
|
||||
|
||||
.cta-description {
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
71
docs/.vitepress/theme/styles/cta.css
Normal file
71
docs/.vitepress/theme/styles/cta.css
Normal file
@@ -0,0 +1,71 @@
|
||||
.cta {
|
||||
margin: 2rem 0;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.cta:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.cta a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1rem 1.5rem;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cta a:hover {
|
||||
color: #333; /* 保持颜色不变 /
|
||||
text-decoration: none; / 移除可能的下划线 */
|
||||
}
|
||||
|
||||
.cta .content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.cta h3 {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cta p {
|
||||
margin: 0.25rem 0 0;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.cta .arrow {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
margin-left: 1rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.cta a:hover .arrow {
|
||||
color: #666; /* 保持箭头颜色不变 */
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
.cta a {
|
||||
background-color: #222;
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.cta a:hover {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
}
|
||||
.cta .arrow {
|
||||
color: #ccc;
|
||||
}
|
||||
.cta a:hover .arrow {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
8
docs/.vitepress/theme/styles/custom.css
Normal file
8
docs/.vitepress/theme/styles/custom.css
Normal file
@@ -0,0 +1,8 @@
|
||||
/* 只影响文档正文里的表格 */
|
||||
.vp-doc table td:nth-child(2),
|
||||
.vp-doc table th:nth-child(2) {
|
||||
white-space: nowrap; /* 第二列不换行 */
|
||||
}
|
||||
|
||||
/* 若想让整张表放宽布局,顺便改一下 ↓ */
|
||||
/* .vp-doc table { table-layout: auto; } */
|
||||
8
docs/.vitepress/theme/styles/image.css
Normal file
8
docs/.vitepress/theme/styles/image.css
Normal file
@@ -0,0 +1,8 @@
|
||||
img[src*="bordered"] {
|
||||
display: block;
|
||||
margin-top: 0.5em;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.2), 0px 4px 6px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 4px;
|
||||
}
|
||||
5
docs/.vitepress/theme/styles/index.css
Normal file
5
docs/.vitepress/theme/styles/index.css
Normal file
@@ -0,0 +1,5 @@
|
||||
@import "./links.css";
|
||||
@import "./cta.css";
|
||||
@import "./cta-container.css";
|
||||
@import "./image.css";
|
||||
|
||||
8
docs/.vitepress/theme/styles/links.css
Normal file
8
docs/.vitepress/theme/styles/links.css
Normal file
@@ -0,0 +1,8 @@
|
||||
:root {
|
||||
--vp-c-brand: #646cff;
|
||||
--vp-c-brand-light: #747bff;
|
||||
}
|
||||
|
||||
.vp-doc a {
|
||||
text-decoration: none;
|
||||
}
|
||||
826
docs/.vitepress/zh.ts
Normal file
826
docs/.vitepress/zh.ts
Normal file
@@ -0,0 +1,826 @@
|
||||
import { defineConfig, type DefaultTheme } from "vitepress";
|
||||
|
||||
const side = {
|
||||
"/zh/manual/": [
|
||||
{
|
||||
text: "文档站",
|
||||
link: "/zh/manual/docs-home",
|
||||
items: [
|
||||
// { text: "应用场景", link: "/zh/manual/why-olares" },
|
||||
//{ text: "功能对比", link: "/zh/manual/feature-overview" },
|
||||
{ text: "系统架构", link: "/zh/manual/system-architecture" },
|
||||
{ text: "比较 Olares 和 NAS", link: "/zh/manual/olares-vs-nas" },
|
||||
{
|
||||
text: "帮助与支持",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "常见问题", link: "/zh/manual/help/faqs" },
|
||||
{
|
||||
text: "技术支持",
|
||||
link: "/zh/manual/help/request-technical-support",
|
||||
},
|
||||
// {
|
||||
// text: "Troubleshooting Guide",
|
||||
// link: "/zh/manual/help/troubleshooting-guide",
|
||||
// },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "快速开始",
|
||||
collapsed: false,
|
||||
link: "/zh/manual/get-started/",
|
||||
items: [
|
||||
// { text: "Quick start", link: "/zh/manual/get-started/quick-start" },
|
||||
{
|
||||
text: "创建 Olares ID",
|
||||
link: "/zh/manual/get-started/create-olares-id",
|
||||
},
|
||||
{
|
||||
text: "安装激活",
|
||||
link: "/zh/manual/get-started/install-olares",
|
||||
},
|
||||
{
|
||||
text: "备份助记词",
|
||||
link: "/zh/manual/larepass/back-up-mnemonics",
|
||||
},
|
||||
{
|
||||
text: "探索",
|
||||
link: "/zh/manual/get-started/next-steps",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "LarePass",
|
||||
link: "/zh/manual/larepass/",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "管理账户",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: "创建账户", link:"/zh/manual/larepass/create-account"},
|
||||
{text: "备份助记词", link: "/zh/manual/larepass/back-up-mnemonics"},
|
||||
{text: "管理集成", link:"/zh/manual/larepass/integrations"},
|
||||
],
|
||||
},
|
||||
{text: "管理专用网络", link:"/zh/manual/larepass/private-network"},
|
||||
{
|
||||
text: "管理设备",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: "激活 Olares", link:"/zh/manual/larepass/activate-olares"},
|
||||
{text: "管理 Olares", link:"/zh/manual/larepass/manage-olares"},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "管理文件",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: "常用文件操作", link:"/zh/manual/larepass/manage-files"},
|
||||
{text: "同步与共享", link:"/zh/manual/larepass/sync-share"}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "管理密码",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: "自动填充", link: "/zh/manual/larepass/autofill"},
|
||||
{text: "双重验证", link: "/zh/manual/larepass/two-factor-verification"},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "管理内容",
|
||||
link: "/zh/manual/larepass/manage-knowledge",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"text": "Olares 应用",
|
||||
"collapsed": true,
|
||||
"link": "/zh/manual/olares/",
|
||||
"items": [
|
||||
{ "text": "桌面", "link": "/zh/manual/olares/desktop" },
|
||||
{ "text": "应用市场", "link": "/zh/manual/olares/market" },
|
||||
{
|
||||
"text": "文件管理器",
|
||||
"collapsed": true,
|
||||
"link": "/zh/manual/olares/files/",
|
||||
"items": [
|
||||
{
|
||||
"text": "基本文件操作",
|
||||
"link": "/zh/manual/olares/files/add-edit-download"
|
||||
},
|
||||
{
|
||||
"text": "同步与共享",
|
||||
"link": "/zh/manual/larepass/sync-share"
|
||||
},
|
||||
{
|
||||
"text": "挂载 SMB",
|
||||
"link": "/zh/manual/olares/files/mount-SMB"
|
||||
},
|
||||
{
|
||||
"text": "挂载云存储",
|
||||
"link": "/zh/manual/olares/files/mount-cloud-storage"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "Vault",
|
||||
"collapsed": true,
|
||||
"link": "/zh/manual/olares/vault/",
|
||||
"items": [
|
||||
{
|
||||
"text": "管理 Vault 项目",
|
||||
"link": "/zh/manual/olares/vault/vault-items"
|
||||
},
|
||||
{
|
||||
"text": "管理共享 Vault",
|
||||
"link": "/zh/manual/olares/vault/share-vault-items"
|
||||
},
|
||||
{
|
||||
"text": "自动填充",
|
||||
"link": "/zh/manual/larepass/autofill"
|
||||
},
|
||||
{
|
||||
"text": "双因素验证",
|
||||
"link": "/zh/manual/larepass/two-factor-verification"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "Wise",
|
||||
"collapsed": true,
|
||||
"link": "/zh/manual/olares/wise/",
|
||||
"items": [
|
||||
{
|
||||
"text": "基本操作",
|
||||
"link": "/zh/manual/olares/wise/basics"
|
||||
},
|
||||
{
|
||||
"text": "获取推荐引擎",
|
||||
"link": "/zh/manual/olares/wise/recommend"
|
||||
},
|
||||
{
|
||||
"text": "管理订阅",
|
||||
"link": "/zh/manual/olares/wise/subscribe"
|
||||
},
|
||||
{
|
||||
"text": "整理知识",
|
||||
"link": "/zh/manual/olares/wise/filter"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "控制面板",
|
||||
"collapsed": true,
|
||||
"link": "/zh/manual/olares/controlhub/",
|
||||
"items": [
|
||||
{
|
||||
"text": "熟悉控制面板",
|
||||
"link": "/zh/manual/olares/controlhub/navigate-control-hub"
|
||||
},
|
||||
{
|
||||
"text": "编辑系统资源",
|
||||
"link": "/zh/manual/olares/controlhub/edit-resource"
|
||||
},
|
||||
{
|
||||
"text": "查看容器状态",
|
||||
"link": "/zh/manual/olares/controlhub/view-container"
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"text": "设置",
|
||||
"collapsed": true,
|
||||
"link": "/zh/manual/olares/settings/",
|
||||
"items": [
|
||||
{
|
||||
"text": "我的 Olares",
|
||||
"collapsed": true,
|
||||
"items": [
|
||||
{text: "账户与设备", link: "/zh/manual/olares/settings/my-olares"},
|
||||
{text: "更新系统", link: "/zh/manual/olares/settings/update"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"text": "管理账户",
|
||||
"collapsed": true,
|
||||
"items": [
|
||||
{
|
||||
"text": "角色与权限",
|
||||
"link": "/zh/manual/olares/settings/roles-permissions",
|
||||
},
|
||||
{
|
||||
"text": "创建成员账户",
|
||||
"link": "/zh/manual/olares/settings/manage-team",
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"text": "管理应用",
|
||||
"collapsed": true,
|
||||
"items": [
|
||||
{
|
||||
"text": "管理应用入口",
|
||||
"link": "/zh/manual/olares/settings/manage-entrance",
|
||||
},
|
||||
{
|
||||
"text": "自定义应用域名",
|
||||
"link": "/zh/manual/olares/settings/custom-app-domain",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"text": "管理集成",
|
||||
"link":"/zh/manual/olares/settings/integrations",
|
||||
},
|
||||
{
|
||||
"text": "自定义外观",
|
||||
"link":"/zh/manual/olares/settings/language-appearance",
|
||||
},
|
||||
{text: "管理 VPN", link: "/zh/manual/olares/settings/remote-access",},
|
||||
{
|
||||
"text": "配置网络",
|
||||
"collapsed": true,
|
||||
"items": [
|
||||
{
|
||||
"text": "更改反向代理",
|
||||
"link": "/zh/manual/olares/settings/change-frp",
|
||||
},
|
||||
{
|
||||
"text": "设置 hosts 文件",
|
||||
"link":"/zh/manual/olares/settings/set-up-hosts",
|
||||
},
|
||||
],
|
||||
},
|
||||
{text: "管理 GPU", link: "/zh/manual/olares/settings/gpu-resource",},
|
||||
{
|
||||
"text": "备份与恢复",
|
||||
"collapsed": true,
|
||||
"items": [
|
||||
{text: "备份", link: "/zh/manual/olares/settings/backup"},
|
||||
{text: "恢复", link: "/zh/manual/olares/settings/restore"},
|
||||
],
|
||||
},
|
||||
{text: "开发者资源", link: "/zh/manual/olares/settings/developer"},
|
||||
]
|
||||
},
|
||||
{ "text": "仪表盘", "link": "/zh/manual/olares/resources-usage" },
|
||||
{ "text": "Profile", "link": "/zh/manual/olares/profile" }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "Olares 进阶",
|
||||
collapsed: true,
|
||||
link: "/zh/manual/best-practices/",
|
||||
items: [
|
||||
{
|
||||
text: "设置自定义域名",
|
||||
link: "/zh/manual/best-practices/set-custom-domain",
|
||||
},
|
||||
{
|
||||
text: "使用 Wise 管理知识",
|
||||
link: "/zh/manual/best-practices/organize-content",
|
||||
},
|
||||
{
|
||||
text: "安装多节点",
|
||||
link: "/zh/manual/best-practices/install-olares-multi-node",
|
||||
},
|
||||
{
|
||||
text: "设置 SMTP",
|
||||
link: "/zh/manual/best-practices/set-up-SMTP-service",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "概念",
|
||||
collapsed: true,
|
||||
link: "/zh/manual/concepts/",
|
||||
items: [
|
||||
{ text: "架构", link: "/zh/manual/concepts/architecture" },
|
||||
{ text: "Olares ID",
|
||||
link: "/zh/manual/concepts/olares-id",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "去中心化标识符",
|
||||
link: "/zh/manual//concepts/did",
|
||||
},
|
||||
{
|
||||
text: "DID Registry",
|
||||
link: "/zh/manual//concepts/registry",
|
||||
},
|
||||
{
|
||||
text: "可验证凭证",
|
||||
link: "/zh/manual//concepts/vc",
|
||||
},
|
||||
{
|
||||
text: "自治声誉",
|
||||
link: "/zh/manual//concepts/reputation",
|
||||
},
|
||||
{
|
||||
text: "主权网络",
|
||||
link: "/zh/manual//concepts/self-sovereign-network",
|
||||
},
|
||||
{
|
||||
text: "身份钱包",
|
||||
link: "/zh/manual/concepts/wallet",
|
||||
},
|
||||
],
|
||||
|
||||
},
|
||||
{ text: "账户", link: "/zh/manual/concepts/account" },
|
||||
{ text: "应用", link: "/zh/manual/concepts/application" },
|
||||
{ text: "网络", link: "/zh/manual/concepts/network" },
|
||||
{ text: "数据", link: "/zh/manual/concepts/data" },
|
||||
{ text: "密钥", link: "/zh/manual/concepts/secrets" },
|
||||
],
|
||||
},
|
||||
{ text: "术语", link: "/zh/manual/glossary" },
|
||||
],
|
||||
"/zh/space/": [
|
||||
{
|
||||
text: "Olares Space",
|
||||
link: "/zh/space/",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "管理账号",
|
||||
link: "/zh/space/manage-accounts",
|
||||
},
|
||||
{
|
||||
text: "托管 Olares",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "创建 Olares",
|
||||
link: "/zh/space/create-olares",
|
||||
},
|
||||
{
|
||||
text: "管理 Olares",
|
||||
link: "/zh/space/manage-olares",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "托管域名",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "设置自定义域名",
|
||||
link: "/zh/space/host-domain",
|
||||
},
|
||||
{
|
||||
text: "管理域名",
|
||||
link: "/zh/space/manage-domain",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "备份与恢复",
|
||||
link: "/zh/space/backup-restore",
|
||||
},
|
||||
{ text: "计费", link: "/zh/space/billing" },
|
||||
],
|
||||
},
|
||||
],
|
||||
"/zh/use-cases/": [
|
||||
{
|
||||
text: "Tutorials & use cases",
|
||||
link: "/zh/use-cases/",
|
||||
items: [
|
||||
{
|
||||
text: "Stable Diffusion",
|
||||
link: "/zh/use-cases/stable-diffusion",
|
||||
},
|
||||
{
|
||||
text: "ComfyUI",
|
||||
link: "/zh/use-cases/comfyui",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Manage ComfyUI",
|
||||
link: "/zh/use-cases/comfyui-launcher",
|
||||
},
|
||||
{
|
||||
text: "Use ComfyUI for Krita",
|
||||
link: "/zh/use-cases/comfyui-for-krita",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "Ollama",
|
||||
link: "/zh/use-cases/ollama",
|
||||
},
|
||||
{
|
||||
text: "Open WebUI",
|
||||
link: "/zh/use-cases/openwebui",
|
||||
},
|
||||
{
|
||||
text: "Perplexica",
|
||||
link: "/zh/use-cases/perplexica",
|
||||
},
|
||||
{
|
||||
text: "Dify",
|
||||
link: "/zh/use-cases/dify",
|
||||
},
|
||||
{
|
||||
text: "Jellyfin",
|
||||
link: "/zh/use-cases/stream-media",
|
||||
},
|
||||
{
|
||||
text: "Steam",
|
||||
link: "/zh/use-cases/stream-game",
|
||||
},
|
||||
{
|
||||
text: "Redroid",
|
||||
link: "/zh/use-cases/host-cloud-android",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"/zh/developer/": [
|
||||
{
|
||||
text: "Olares 安装详解",
|
||||
link: "/zh/developer/install/",
|
||||
items: [
|
||||
{
|
||||
text: "安装概述",
|
||||
link: "/zh/developer/install/installation-overview",
|
||||
},
|
||||
{
|
||||
text: "安装流程",
|
||||
link: "/zh/developer/install/installation-process",
|
||||
},
|
||||
{
|
||||
text: "Olares Home",
|
||||
link: "/zh/developer/install/olares-home",
|
||||
},
|
||||
{
|
||||
text: "环境变量",
|
||||
link: "/zh/developer/install/environment-variables",
|
||||
},
|
||||
{
|
||||
text: "Olares CLI",
|
||||
collapsed: true,
|
||||
link: "/zh/developer/install/cli/olares-cli",
|
||||
items: [
|
||||
{ text: "gpu", link: "/zh/developer/install/cli/gpu" },
|
||||
{ text: "osinfo", link: "/zh/developer/install/cli/osinfo" },
|
||||
{ text: "node", link: "/zh/developer/install/cli/node" },
|
||||
{
|
||||
text: "backups",
|
||||
link: "/zh/developer/install/cli/backups",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{text: "download", link: "/zh/developer/install/cli/backups-download"},
|
||||
{text: "region", link: "/zh/developer/install/cli/backups-region"},
|
||||
{text: "backup", link: "/zh/developer/install/cli/backups-backup"},
|
||||
{text: "restore", link: "/zh/developer/install/cli/backups-restore"},
|
||||
{text: "snapshots", link: "/zh/developer/install/cli/backups-snapshots"},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "change-ip",
|
||||
link: "/zh/developer/install/cli/change-ip",
|
||||
},
|
||||
{
|
||||
text: "download",
|
||||
link: "/zh/developer/install/cli/download",
|
||||
},
|
||||
{ text: "info", link: "/zh/developer/install/cli/info" },
|
||||
{
|
||||
text: "install",
|
||||
link: "/zh/developer/install/cli/install",
|
||||
},
|
||||
{
|
||||
text: "logs",
|
||||
link: "/zh/developer/install/cli/logs",
|
||||
},
|
||||
{
|
||||
text: "precheck",
|
||||
link: "/zh/developer/install/cli/precheck",
|
||||
},
|
||||
{
|
||||
text: "prepare",
|
||||
link: "/zh/developer/install/cli/prepare",
|
||||
},
|
||||
{
|
||||
text: "release",
|
||||
link: "/zh/developer/install/cli/release",
|
||||
},
|
||||
{
|
||||
text: "start",
|
||||
link: "/zh/developer/install/cli/start",
|
||||
},
|
||||
{
|
||||
text: "stop",
|
||||
link: "/zh/developer/install/cli/stop",
|
||||
},
|
||||
{
|
||||
text: "uninstall",
|
||||
link: "/zh/developer/install/cli/uninstall",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "版本说明",
|
||||
link: "/zh/developer/install/versioning",
|
||||
},
|
||||
// {
|
||||
// text: "其他安装方式",
|
||||
// link: "/zh/developer/install/additional-installations",
|
||||
// collapsed: true,
|
||||
// items: [
|
||||
// { text: "Linux(Docker 镜像)", link: "/zh/developer/install/linux-via-docker-compose" },
|
||||
// {
|
||||
// text: "macOS",
|
||||
// collapsed: true,
|
||||
// items: [
|
||||
// {
|
||||
// text: "使用脚本(推荐)",
|
||||
// link: "/zh/developer/install/mac",
|
||||
// },
|
||||
// {
|
||||
// text: "使用 Docker 镜像",
|
||||
// link: "/zh/developer/install/mac-via-docker-image",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// text: "Windows (WSL 2)",
|
||||
// collapsed: true,
|
||||
// items: [
|
||||
// {
|
||||
// text: "使用脚本(推荐)",
|
||||
// link: "/zh/developer/install/windows",
|
||||
// },
|
||||
// {
|
||||
// text: "使用 Docker 镜像",
|
||||
// link: "/zh/developer/install/windows-via-docker-image",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// { text: "PVE", link: "/zh/developer/install/pve" },
|
||||
// { text: "LXC", link: "/zh/developer/install/lxc" },
|
||||
// { text: "树莓派", link: "/zh/developer/install/raspberry-pi" },
|
||||
// ],
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "开发 Olares 应用",
|
||||
link: "/zh/developer/develop/",
|
||||
items: [
|
||||
{
|
||||
text: "教程",
|
||||
collapsed: true,
|
||||
link: "/zh/developer/develop/tutorial/",
|
||||
items: [
|
||||
{
|
||||
text: "了解 Studio",
|
||||
link: "/zh/developer/develop/tutorial/studio",
|
||||
},
|
||||
{
|
||||
text: "创建首个应用",
|
||||
collapsed: true,
|
||||
link: "/zh/developer/develop/tutorial/note/",
|
||||
items: [
|
||||
{
|
||||
text: "1. 创建应用",
|
||||
link: "/zh/developer/develop/tutorial/note/create",
|
||||
},
|
||||
{
|
||||
text: "2. 开发后端",
|
||||
link: "/zh/developer/develop/tutorial/note/backend",
|
||||
},
|
||||
{
|
||||
text: "3. 开发前端",
|
||||
link: "/zh/developer/develop/tutorial/note/frontend",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "应用包管理",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "应用 Chart 包",
|
||||
link: "/zh/developer/develop/package/chart",
|
||||
},
|
||||
{
|
||||
text: "OlaresManifest",
|
||||
link: "/zh/developer/develop/package/manifest",
|
||||
},
|
||||
{
|
||||
text: "推荐算法",
|
||||
link: "/zh/developer/develop/package/recommend",
|
||||
},
|
||||
{
|
||||
text: "Helm 扩展",
|
||||
link: "/zh/developer/develop/package/extension",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "进阶",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "terminus-info",
|
||||
link: "/zh/developer/develop/advanced/terminus-info",
|
||||
},
|
||||
{
|
||||
text: "Service Provider",
|
||||
link: "/zh/developer/develop/advanced/provider",
|
||||
},
|
||||
{
|
||||
text: "AI",
|
||||
link: "/zh/developer/develop/advanced/ai",
|
||||
},
|
||||
{ text: "Cookie", link: "/zh/developer/develop/advanced/cookie" },
|
||||
{ text: "数据库", link: "/zh/developer/develop/advanced/database" },
|
||||
{
|
||||
text: "账户",
|
||||
link: "/zh/developer/develop/advanced/account",
|
||||
},
|
||||
{
|
||||
text: "应用市场",
|
||||
link: "/zh/developer/develop/advanced/market",
|
||||
},
|
||||
// {
|
||||
// text: "Analytic",
|
||||
// link: "/zh/developer/develop/advanced/analytic",
|
||||
// },
|
||||
{
|
||||
text: "Websocket",
|
||||
link: "/zh/developer/develop/advanced/websocket",
|
||||
},
|
||||
{
|
||||
text: "文件上传",
|
||||
link: "/zh/developer/develop/advanced/file-upload",
|
||||
},
|
||||
// {
|
||||
// text: "Rss",
|
||||
// link: "/zh/developer/develop/advanced/rss",
|
||||
// },
|
||||
{
|
||||
text: "密钥",
|
||||
link: "/zh/developer/develop/advanced/secret",
|
||||
},
|
||||
// {
|
||||
// text: "Notification",
|
||||
// link: "/zh/developer/develop/advanced/notification",
|
||||
// },
|
||||
// {
|
||||
// text: "Frontend",
|
||||
// link: "/zh/developer/develop/advanced/frontend",
|
||||
// },
|
||||
{
|
||||
text: "Kubesphere",
|
||||
link: "/zh/developer/develop/advanced/kubesphere",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
text: "提交应用",
|
||||
collapsed: true,
|
||||
link: "/zh/developer/develop/submit/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "参与贡献",
|
||||
items: [
|
||||
{
|
||||
text: "开发系统应用",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "概述",
|
||||
link: "/zh/developer/contribute/system-app/overview",
|
||||
},
|
||||
{
|
||||
text: "应用部署配置",
|
||||
link: "/zh/developer/contribute/system-app/deployment",
|
||||
},
|
||||
{
|
||||
text: "Olares 权限配置",
|
||||
link: "/zh/developer/contribute/system-app/olares-manifest",
|
||||
},
|
||||
{
|
||||
text: "安装",
|
||||
link: "/zh/developer/contribute/system-app/install",
|
||||
},
|
||||
{
|
||||
text: "其他",
|
||||
link: "/zh/developer/contribute/system-app/other",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "开发协议",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "合约",
|
||||
link: "/zh/developer/contribute/olares-id/contract/contract",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "架构",
|
||||
link: "/zh/developer/contribute/olares-id/contract/architecture",
|
||||
},
|
||||
{
|
||||
text: "DID",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "设计",
|
||||
link: "/zh/developer/contribute/olares-id/contract/did/design",
|
||||
},
|
||||
{
|
||||
text: "官方 Tagger",
|
||||
link: "/zh/developer/contribute/olares-id/contract/did/official-taggers",
|
||||
},
|
||||
{
|
||||
text: "发布历史",
|
||||
link: "/zh/developer/contribute/olares-id/contract/did/release-history",
|
||||
},
|
||||
{
|
||||
text: "FAQ",
|
||||
link: "/zh/developer/contribute/olares-id/contract/did/faq",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "声誉",
|
||||
link: "/zh/developer/contribute/olares-id/contract/contract-reputation",
|
||||
},
|
||||
{
|
||||
text: "管理",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "合约",
|
||||
link: "/zh/developer/contribute/olares-id/contract/manage/contract",
|
||||
},
|
||||
{
|
||||
text: "SDK",
|
||||
link: "/zh/developer/contribute/olares-id/contract/manage/sdk",
|
||||
},
|
||||
{
|
||||
text: "环境",
|
||||
link: "/zh/developer/contribute/olares-id/contract/manage/environment",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "可验证凭证(VC)",
|
||||
link: "/zh/developer/contribute/olares-id/verifiable-credential/overview",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "发行方",
|
||||
link: "/zh/developer/contribute/olares-id/verifiable-credential/issuer",
|
||||
},
|
||||
{
|
||||
text: "验证方",
|
||||
link: "/zh/developer/contribute/olares-id/verifiable-credential/verifer",
|
||||
},
|
||||
{
|
||||
text: "Olares 案例",
|
||||
link: "/zh/developer/contribute/olares-id/verifiable-credential/olares",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const zh = defineConfig({
|
||||
lang: "zh",
|
||||
themeConfig: {
|
||||
//logo: "/icon.png",
|
||||
socialLinks: [{ icon: "github", link: "https://github.com/beclab/olares" }],
|
||||
|
||||
nav: [
|
||||
{ text: "Olares", link: "zh/manual/docs-home" },
|
||||
{ text: "Olares Space", link: "/zh/space/" },
|
||||
{ text: "应用示例", link: "/zh/use-cases/" },
|
||||
{ text: "开发者文档", link: "/zh/developer/install/" },
|
||||
],
|
||||
|
||||
sidebar: side,
|
||||
},
|
||||
});
|
||||
201
docs/LICENSE
Normal file
201
docs/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,3 +1,59 @@
|
||||
# Olares Doc
|
||||
# Olares documentation
|
||||
|
||||
https://github.com/beclab/docs
|
||||
Welcome to the source repository for the official Olares documentation!
|
||||
|
||||
This directory includes the source files for the Olares documentation website, instructions for setting up the project locally, Markdown references, and style guides to ensure consistency and quality across all documentation.
|
||||
|
||||
## Quick links
|
||||
|
||||
* **Published documentation site**: https://docs.olares.com
|
||||
* **Olares official website**: https://www.olares.com
|
||||
* **Olares project on GitHub**: https://github.com/beclab/Olares
|
||||
|
||||
## Getting started with Olares documentation
|
||||
|
||||
We welcome community contributions! Follow these steps to preview, develop, and build the documentation locally.
|
||||
|
||||
### Install dependencies
|
||||
|
||||
First, ensure you have [Node.js](https://nodejs.org/) installed (LTS version recommended). Then, run the following command in the project's root directory to install the required dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
````
|
||||
|
||||
### Start the development server
|
||||
|
||||
Start the local development server with hot-reloading. This allows you to see your changes live in the browser as you edit the source files.
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Once running, open your browser and navigate to `http://localhost:5173/` to see the local preview.
|
||||
|
||||
### Build the site locally
|
||||
|
||||
To generate a production-ready build for final review or debugging, run:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
This command will build the static site into the `dist` directory.
|
||||
|
||||
## Versioning and branching strategy
|
||||
|
||||
To manage documentation for different product versions effectively, we use the following branching strategy:
|
||||
|
||||
* **`main` branch**:
|
||||
This branch contains the latest documentation for the **next, in-development version** of Olares. All documentation for new features should be submitted here.
|
||||
|
||||
* **`release-{version}` branch**:
|
||||
These branches hold the documentation for **recent, stable versions**. For example, the `release-1.11` branch corresponds to the `Olares 1.11` documentation. Fixes or clarifications for a specific stable version should be submitted to its corresponding release branch.
|
||||
|
||||
Creating a PR to a corresponding branch will automatically trigger the documentation build for that version.
|
||||
|
||||
## Style guide
|
||||
|
||||
To ensure clarity, accuracy, and a consistent tone, please read our **[Content and style guide](https://github.com/beclab/Olares/wiki/General-style-reference)** before contributing.
|
||||
31
docs/code-snippets/docker-compose-GPU.yaml
Normal file
31
docs/code-snippets/docker-compose-GPU.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
services:
|
||||
olares:
|
||||
image: beclab/olares:${VERSION}
|
||||
privileged: true
|
||||
volumes:
|
||||
- oic-data:/var
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "30180:30180"
|
||||
- "18088:18088"
|
||||
- "41641:41641/udp"
|
||||
environment:
|
||||
- HOST_IP=${HOST_IP}
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: 1
|
||||
capabilities: [gpu]
|
||||
|
||||
olaresd-proxy:
|
||||
image: beclab/olaresd:proxy-v0.1.0
|
||||
network_mode: host
|
||||
depends_on:
|
||||
olares:
|
||||
condition: service_started
|
||||
|
||||
volumes:
|
||||
oic-data:
|
||||
24
docs/code-snippets/docker-compose.yaml
Normal file
24
docs/code-snippets/docker-compose.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
services:
|
||||
olares:
|
||||
image: beclab/olares:${VERSION}
|
||||
privileged: true
|
||||
volumes:
|
||||
- oic-data:/var
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "30180:30180"
|
||||
- "18088:18088"
|
||||
- "41641:41641/udp"
|
||||
environment:
|
||||
- HOST_IP=${HOST_IP}
|
||||
|
||||
olaresd-proxy:
|
||||
image: beclab/olaresd:proxy-v0.1.0
|
||||
network_mode: host
|
||||
depends_on:
|
||||
olares:
|
||||
condition: service_started
|
||||
|
||||
volumes:
|
||||
oic-data:
|
||||
9
docs/code-snippets/docker-daemon.json
Normal file
9
docs/code-snippets/docker-daemon.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://mirrors.joinolares.cn"
|
||||
],
|
||||
|
||||
"features": {
|
||||
"containerd-snapshotter": false
|
||||
}
|
||||
}
|
||||
52
docs/compress_images.sh
Normal file
52
docs/compress_images.sh
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Check if pngquant and jpegoptim are installed
|
||||
if ! command -v pngquant &> /dev/null; then
|
||||
echo "pngquant is not installed. Please install it first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v jpegoptim &> /dev/null; then
|
||||
echo "jpegoptim is not installed. Please install it first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create base compress directory if it doesn't exist
|
||||
mkdir -p compress
|
||||
|
||||
# Function to process a single file
|
||||
process_file() {
|
||||
local source_file="$1"
|
||||
# Remove 'public/' prefix and prepend 'compress/'
|
||||
local target_file="compress/${source_file#public/}"
|
||||
# Get the directory path of the target file
|
||||
local target_dir="$(dirname "$target_file")"
|
||||
|
||||
# Create target directory if it doesn't exist
|
||||
mkdir -p "$target_dir"
|
||||
|
||||
# Copy file first to preserve original
|
||||
cp "$source_file" "$target_file"
|
||||
|
||||
# Process based on file extension
|
||||
local ext=$(echo "${source_file##*.}" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Process based on file extension
|
||||
case "$ext" in
|
||||
png)
|
||||
echo "Compressing PNG: $source_file"
|
||||
pngquant --quality=90-95 --skip-if-larger --force --output "$target_file" "$target_file"
|
||||
;;
|
||||
jpg|jpeg)
|
||||
echo "Compressing JPEG: $source_file"
|
||||
jpegoptim --max=95 --preserve --strip-none "$target_file"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Find and process all PNG and JPEG files
|
||||
find public -type f \( -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.svg" \) | while read -r file; do
|
||||
process_file "$file"
|
||||
done
|
||||
|
||||
echo "Image compression completed. Compressed images are in the 'compress' directory."
|
||||
121
docs/developer/contribute/olares-id/contract/architecture.md
Normal file
121
docs/developer/contribute/olares-id/contract/architecture.md
Normal file
@@ -0,0 +1,121 @@
|
||||
---
|
||||
outline: [2, 4]
|
||||
---
|
||||
|
||||
# TerminusDID Contract System
|
||||
|
||||
## Architecture
|
||||
|
||||
The TerminusDID Contract System is divided into two parts: DID Management and Tag Management. In addition to the core functions for Tag Management, we also implemented an official tagger and reputation system.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
|
||||
RootTagger{RootTagger}
|
||||
|
||||
RSAPubKey[/RSAPubKey/]
|
||||
DNSARecord[/DNSARecord/]
|
||||
AuthAddress[/AuthAddress/]
|
||||
otherTag[/.../]
|
||||
|
||||
|
||||
|
||||
|
||||
TerminusDID{TerminusDID}
|
||||
Reputations[/Reputations\]
|
||||
XXXReputation{...}
|
||||
AppMarketReputation{AppMarketReputation}
|
||||
Tag[[Tag]]
|
||||
Tagger([Tagger])
|
||||
|
||||
|
||||
DID[DID]
|
||||
Domain((Domain))
|
||||
com((com))
|
||||
net((net))
|
||||
io((io))
|
||||
|
||||
TerminusDID--->DID
|
||||
TerminusDID--->Tag
|
||||
Tag--->Tagger
|
||||
|
||||
Tagger-.-RootTagger
|
||||
Tagger-.-AppMarketReputation
|
||||
Tagger-.-XXXReputation
|
||||
|
||||
|
||||
Domain-.->Tag
|
||||
|
||||
|
||||
subgraph ide4 [Tag]
|
||||
|
||||
subgraph ide1 [OfficialTag]
|
||||
RootTagger--->RSAPubKey
|
||||
RootTagger--->DNSARecord
|
||||
RootTagger--->AuthAddress
|
||||
RootTagger--->otherTag
|
||||
end
|
||||
|
||||
subgraph ide2 [Reputations]
|
||||
Reputations-.->AppMarketReputation
|
||||
Reputations-.->XXXReputation
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
subgraph ide3 [DID]
|
||||
DID-.-Domain
|
||||
Domain--->com
|
||||
Domain--->net
|
||||
Domain--->io
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
Refer [here](./contract.md) for the usage of DID/Tag Management, [here](././did/design.md) for design details of the TerminusDID contract, [here](./contract-reputation.md) for recommended implementations and examples of the reputation system.
|
||||
|
||||
## Design Details
|
||||
|
||||
### Multicall
|
||||
|
||||
Considering the complexity and the uncertainty of usage scenarios of our contracts, we add built-in multicall function to simplify on-chain interaction. You can access multiple interfaces in one transaction without external helper contracts.
|
||||
|
||||
Considering the complexity and uncertainty of usage scenarios of our contracts, we have added a built-in multicall function to simplify on-chain interactions. You can access multiple interfaces in one transaction without external contracts.
|
||||
|
||||
|
||||
### EIP-7201
|
||||
|
||||
We follow EIP-7201 for contract data storage, which makes it easier and safer for contract upgrade and also facilitates granular control of data.
|
||||
|
||||
### Validation of Olares ID
|
||||
|
||||
Olares ID is submitted as a `string` type during registration. Although it is split by `.` for level separation, this cannot guarantee its correctness and readability. We implemented additional validation in contract code to ensure the submitted names are UTF-8-encoded readable strings.
|
||||
|
||||
### Field Names of Structs in Tags
|
||||
|
||||
For gas efficiency, if there are structs in a tag type, their field names are published on-chain using Ethereum events without contract storage. The events will record the block height at which the tag type is defined. Use the block height, contract address, event signature and Bloom filters to get the desired event precisely.
|
||||
|
||||
### Inline Assembly
|
||||
|
||||
We use slice types based on inline assembly to parse and traverse Olares ID levels, which can prevent copying substrings and reduce gas.
|
||||
|
||||
## Appendix - Requirements
|
||||
|
||||
### DID
|
||||
|
||||
- on-chain [DID](https://www.w3.org/TR/did-core/) management (prefer EVM-compatible chains)
|
||||
- recording IPv4/IPv6, avatar, RSA public key etc. for DIDs
|
||||
- extending stored data for possible new needs in the future
|
||||
|
||||
### Olares ID
|
||||
|
||||
- distinguishing between two kinds of DIDs for Olares ID: Organization or Individual
|
||||
- derived DID management privilege for parent Organization
|
||||
|
||||
### Reputation
|
||||
|
||||
DIDs with another kind called Entity to represent real-world objects (e.g. app versions for App Market Reputation and complaints for Otmoic Reputation)
|
||||
|
||||
:::tip Tips
|
||||
We hope to design a decentralized credit system based on DID contracts. At first we imagined a general system adaptable to various scenarios, but with its gradually implementation we found this large and comprehensive system to bring much unnecessary resource consumption and reduce flexibility in different scenarios. So we changed the direction: we present a Reputation system for abstraction and necessary components and provide some recommended implementations. Users can assembly and customize the implementation to satisfy their own scenarios.
|
||||
:::
|
||||
@@ -0,0 +1,98 @@
|
||||
# Reputation
|
||||
|
||||
#### Extra requirements: Abstract Reputation System
|
||||
|
||||
:::info Required features
|
||||
|
||||
- identifiers that map to real-world objects
|
||||
All credit systems are based on some specific scenarios, e.g. reviews for an app or complaints for a merchant. This inevitably needs us to build unique on-chain identifiers for some real-world or abstract objects.
|
||||
|
||||
- authentication
|
||||
The most core function of the credit system is reviewing and reviewer authentication is one of its prerequisites.
|
||||
|
||||
- storage of necessary information
|
||||
After user reviewing, we may need to record the comments or calculate some weighted statistical data.
|
||||
|
||||
- recording of history and updating
|
||||
Under some circumstances, we may need to record the behavior of reviewing and allow updating existing reviews in the future.
|
||||
:::
|
||||
|
||||
The corresponding solution for each requirement is provided in this chapter.
|
||||
|
||||
## Objects to be Reviewed
|
||||
|
||||
For the objects to be reviewed, the following two situations may occur:
|
||||
|
||||
- it's a merchant or a single person
|
||||
In this case the object must have a DID of kind Individual, so we can operate the object directly without creating other DIDs.
|
||||
- it's a real-world object or abstract concept
|
||||
In this case we need to first create a DID of kind Entity for this object and then operate on it.
|
||||
|
||||
## Authentication
|
||||
|
||||
There are two solutions for authentication:
|
||||
|
||||
- Use the owner of the DID to submit tx and let the tagger call interfaces of the Terminus DID contract for authentication.
|
||||
- Use the owner of the DID to sign a customized EIP-712 message and use a forwarder to send tx on-chain. The tagger will use the message signer for authentication.
|
||||
|
||||
> [!NOTE] Tips
|
||||
> We recommend the second solution as the transaction fee is paid by forwarders instead of reviewers.
|
||||
|
||||
## Storage of Necessary Information
|
||||
|
||||
For DIDs of kind Entity, we define tags of this DID itself and write necessary data to them. For DIDs of kind Individual, we abstract the scenario as a DID of kind Entity and write to its tags.
|
||||
|
||||
## Recording of History and Updating
|
||||
|
||||
We can follow the advice in the above section and store data in tags of Entity. But on-chain querying is not required in some scenarios, where on-chain storage is a waste. For those cases we recommend to use Ethereum events for recording and customize the detailed implementation in taggers.
|
||||
|
||||
## Example - OtmoicReputation
|
||||
|
||||
:::info
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
|
||||
otmoic{{OtmoicReputation}}
|
||||
did{{TerminusDID}}
|
||||
complaints[/tag-complaints/]
|
||||
otmoicdid((otmoic.reputation))
|
||||
|
||||
otmoic-- Authentication -->did
|
||||
did-.->otmoicdid
|
||||
otmoicdid-.-complaints
|
||||
otmoic-- read/write -->complaints
|
||||
```
|
||||
|
||||
The Otmoic Reputation contract uses EIP-712 signature of DID owners for authentication and stores the `bidid` of the complained bid in the `complaints` tag of Entity `otmoic.reputation`.
|
||||
:::
|
||||
|
||||
## Example - TerminusAppMarketReputation
|
||||
|
||||
:::info
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
|
||||
app((app.myterminus.com))
|
||||
appname((appname.app.myterminus.com))
|
||||
version1((version1.appname.app.myterminus.com))
|
||||
version2((version2.appname.app.myterminus.com))
|
||||
reputation{{TerminusAppMarketReputation}}
|
||||
ratings[/tag-ratings/]
|
||||
version1ratings[/version1-tag-ratings/]
|
||||
event[[event]]
|
||||
|
||||
app-->appname
|
||||
appname-->version1
|
||||
appname-->version2
|
||||
app-.->ratings
|
||||
ratings-.-version1ratings
|
||||
version1-.-version1ratings
|
||||
reputation-.- read/write -.->version1ratings
|
||||
reputation-.->event
|
||||
|
||||
```
|
||||
|
||||
The Olares App Market Reputation contract also uses EIP-712 signature of DID owners for authentication. Rating data of the contract is stored in the `ratings` tag of Entity `<version>.<appname>.app.myterminus.com`, and comment data is published in the form of Ethereum events instead of storing on-chain.
|
||||
:::
|
||||
229
docs/developer/contribute/olares-id/contract/contract.md
Normal file
229
docs/developer/contribute/olares-id/contract/contract.md
Normal file
@@ -0,0 +1,229 @@
|
||||
---
|
||||
outline: [1, 5]
|
||||
---
|
||||
|
||||
# Smart Contract
|
||||
|
||||
Snowinning Protocol's smart contract has two parts.
|
||||
|
||||
- [TerminusDID](https://github.com/beclab/terminusdid-contract-system/blob/main/src/core/TerminusDID.sol) contract plays a crucial role as the [DID Registry](/manual/concepts/registry.md). Learn more in [the contract](https://optimistic.etherscan.io/address/0x5da4fa8e567d86e52ef8da860de1be8f54cae97d).
|
||||
- Third-party protocols that extend the reputation system based on [TerminusDID](https://github.com/beclab/terminusdid-contract-system/blob/main/src/core/TerminusDID.sol). Currently, the following reputation protocols are in place:
|
||||
- [Otmoic Trader Reputation](https://github.com/otmoic/reputation-contract-evm/blob/main/contracts/Reputation.sol). Learn more in [the contract](https://optimistic.etherscan.io/address/0x3179CE5fAB68C0286Da85f3d61BcE7116815e799).
|
||||
- [Application Reputation](https://github.com/beclab/terminusdid-contract-system/blob/main/src/taggers/TerminusAppMarketReputation.sol). Learn more in [the contract](https://optimistic.etherscan.io/address/0x08065353D266121938B93D4B1071Bb52CD0C0EE4).
|
||||
|
||||

|
||||
|
||||
- The [TerminusDID](https://github.com/beclab/terminusdid-contract-system/blob/main/src/core/TerminusDID.sol) contract plays a crucial role, and it serves as the [DID Registry](/manual/concepts/registry.md). View [the contract](https://optimistic.etherscan.io/address/0x5da4fa8e567d86e52ef8da860de1be8f54cae97d).
|
||||
- Third-party protocols can extend the reputation based on [TerminusDID](https://github.com/beclab/terminusdid-contract-system/blob/main/src/core/TerminusDID.sol). Currently, the following reputation protocols are in place:
|
||||
- [Otmoic Trader Reputation](https://github.com/otmoic/reputation-contract-evm/blob/main/contracts/Reputation.sol). View [the contract](https://optimistic.etherscan.io/address/0xE924F7f68D1dcd004720e107F62c6303aF271ed3).
|
||||
- [Application Reputation](https://github.com/beclab/terminusdid-contract-system/blob/main/src/taggers/TerminusAppMarketReputation.sol). View [the contract](https://optimistic.etherscan.io/address/0x08065353D266121938B93D4B1071Bb52CD0C0EE4).
|
||||
|
||||
|
||||
|
||||
# TerminusDID
|
||||
|
||||
The TerminusDID contract manages a hierarchical structure derived from [Domain](/manual/concepts/olares-id.md#domain-types.
|
||||
|
||||

|
||||
|
||||
## Node
|
||||
|
||||
Each node possesses several default attributes.
|
||||
|
||||
| Attribute | Description |
|
||||
| -------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| name | Specifies the [Domain Name](/manual/concepts/olares-id.md#domain-types). Certain Domain Names can interchange with Terminus Names |
|
||||
| id | Each node is also a NFT, adhering to the **ERC-721** standard. Its id serves as the unique identifier for this NFT, computed through `keccak256(name)` |
|
||||
| did,owner | The `owner` and `did` of the node, derived from the same mnemonic words. Further details on this can be found [here](/manual/concepts/did.md).<br>. Additionally, storing owner is advantageous because it complies with the **BIP44** specification, facilitating gas-efficient signature verification within EVM contracts. Each node is affiliated with an `owner`, who holds the authority to modify node details. |
|
||||
| note | Currently, there are three types: Individual, Organization, and Entity |
|
||||
| allowSubdomain | Indicates whether it is a leaf node. If False, the node cannot spawn further nodes. |
|
||||
|
||||
The following is an illustrative example that specifies the default attributes of a node:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "0xbf90de759829bfa3d2b10e4c4a7101e3b116f70b9a5431951ff9d83a4c8e3ceb",
|
||||
"name": "olivia.myterminus.com",
|
||||
"did": "did:key:z6MkuhT5kYegHaJxirMaHxuqzJhGZk5r32Ct5nYVRQWpN31q",
|
||||
"note": "Individual",
|
||||
"owner": "0x1404C95b2Cb2E46E03650bc02f83364A92f8DA9D",
|
||||
"allowSubdomain": true
|
||||
}
|
||||
```
|
||||
|
||||
## Owner
|
||||
|
||||
Ownership of different nodes is as follows:
|
||||
|
||||
- **System**<br>
|
||||
Abstract nodes such as `root`, `com`, `io` belong to the Terminus team.
|
||||
|
||||
- **Individual** <br>
|
||||
`myterminus.com` belongs to the [Individual Domain](/manual/concepts/olares-id.md#domain-types), owned by the Terminus team.
|
||||
`alice.myterminus.com` and `bob.myterminus.com` belong to individual Terminus Names, and are owned by the respective users.
|
||||
|
||||
- **Organization** <br>
|
||||
`org1.com` and `org.io` belong to the [Organization Domain](/manual/concepts/olares-id.md#domain-types), owned by the domain admin.
|
||||
`alice.org1.com` and `bob.org2.io` belong to Organization Terminus Names, and are owned by the respective users.
|
||||
|
||||
- **Entity** <br>
|
||||
The `Application Score` belongs to the [Entity Domain](/manual/concepts/olares-id.md#domain-types), and is owned by the applicant of the entity. Organization admins and users can refer to [Domain Management](../contract/manage/contract.md#register-did) to manage their own nodes and sub-nodes.
|
||||
|
||||
:::info
|
||||
After the project stabilizes, ownership will be transferred to the multisig address of the DAO organization by the Terminus team.
|
||||
:::
|
||||
|
||||
## Tag
|
||||
|
||||
The [Tag](https://github.com/beclab/terminusdid-contract-system/blob/main/src/core/TagRegistry.sol) mechanism allows the [TerminusDID](https://github.com/beclab/terminusdid-contract-system/blob/main/src/core/TerminusDID.sol) contract to extend the metadata stored on nodes.
|
||||
|
||||
For example, if you are an enterprise managing an organizational domain, and you want to add the attribute employee-id to each employee under the organization, you need to extend based on Tags.
|
||||
|
||||
Tag system allows you to freely store data types such as `uint`, `int`, `address`, `boolean`, `bytes`, `string`, `structure`, `fixed length array`, `variable length array` on-chain in a specific encoding format (ABI). Before reading or writing data, you need to define the data type. The data type you define will apply to itself and all its child nodes. The information of **definer, user, and Tag name** is used to index a unique piece of data.
|
||||
|
||||
:::info Note
|
||||
|
||||
For complex structures or arrays, as well as cases where complex structures and arrays are nested within each other, if data is written in units of Tags each time, it will result in huge useless gas consumption. Therefore, we have implemented the functionality of updating a single field or operating a single array separately in the system. When performing single updates, in addition to **definer, user, and Tag name**, you also need to provide the **path** of the data, which is the variable name inside the structure.
|
||||
:::
|
||||
|
||||
### Customized Tags
|
||||
|
||||
| Field | Description |
|
||||
| ---------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| name | Name of this tag |
|
||||
| did | The DID that defines this tag |
|
||||
| abiType | The data type of this tag, following the encoding specifications of **abi**, also supports defining a complex structure |
|
||||
| fieldNames | When defining a complex structure, the names of internal substructures or data are flattened in a two-dimensional array |
|
||||
|
||||
|
||||
::: tip NOTE
|
||||
Owner/did as a custom tag is only effective for the node and its sub nodes.
|
||||
:::
|
||||
|
||||
### Tagger
|
||||
|
||||
Tagger is one of the necessary pieces of information inside each Tag. It represents the unique entity with permission to modify the Tag value, which can be a wallet address or a contract. Tagger may change frequently.
|
||||
|
||||
|
||||
> [!NOTE]
|
||||
> We recommend that you use contracts as Taggers.
|
||||
>
|
||||
> - Referring to the official implementations of several Taggers, you can utilize DID contracts for comprehensive operator identity authentication and also achieve more refined custom permission controls.
|
||||
> - For complex Tag structures or data content with special specifications, setting the Tagger as a contract can verify the data format on-chain or establish more comprehensive custom rules. For example, for the value of RSAPubKey in the official Tag, we perform validation of bytes data in Pkcs8 ASN.1 format on-chain to prevent setting values that cannot be parsed.
|
||||
|
||||
## Use Cases
|
||||
|
||||
Some Taggers are provided under the Root node:
|
||||
|
||||
- RSAPubKey: Users can declare RSA public keys, and third parties can send them private messages based on this public key.
|
||||
- AuthAddresses: Users can declare the wallet addresses they own.
|
||||
- DNSARecord: Users can declare the DNS A records of Edge nodes.
|
||||
|
||||
:::info About AuthAddresses
|
||||
The following systems rely on this Tag's data operation:
|
||||
|
||||
- Setting an Avatar
|
||||
|
||||
When setting an avatar in the system, you can choose any NFT image you own as an avatar. The standard for determining if you own an NFT is whether the address of the owner exists in AuthAddresses.
|
||||
|
||||
- Otmoic lp
|
||||
|
||||
Otmoic lp operates as an account with TerminusName. When signing transaction information, it verifies whether the signer's address exists in AuthAddresses.
|
||||
|
||||
Because AuthAddresses involve another wallet, simply verifying the sender of the transaction is not credible. Therefore, we have designed the following operation plan:
|
||||
|
||||
Use the declared address and DID owner to sign the following information in compliance with the EIP712 standard and submit it to the contract for verification within a limited time (30 minutes after signing).
|
||||
|
||||
```json
|
||||
{
|
||||
"address": "0x10FE2771907B0c4245695daD7e9Ed064d45860f8",
|
||||
"algorithm": 0, // 0: ECDSA
|
||||
"domain": "olivia.myterminus.com",
|
||||
"signAt": "1714287578",
|
||||
"action": 0 //0: Add 1: Remove
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
|
||||
# Reputation
|
||||
|
||||
We can create highly flexible [reputation](/manual/concepts/reputation.md) protocols based on Taggers.
|
||||
|
||||
In implementing an on-chain Reputation system, the most crucial elements are:
|
||||
|
||||
- Abstracting and representing the object to be evaluated
|
||||
- Evaluating the object and storing necessary information
|
||||
- Authenticating the evaluator's identity
|
||||
|
||||
Regarding these elements, TerminusDID provides a comprehensive solution. We can use Individual and Entity types of DID to represent the object to be evaluated, use Tags to store extended metadata, and utilize TerminusDID's built-in permission management features to verify identities. Learn more in the following two cases.
|
||||
|
||||
## Otmoic Trader Reputation
|
||||
|
||||
Otmoic's reputation contract uses DID owner's EIP712 signature for identity verification, and then stores the complain did in the `complaints` field of the `otmoic.reputation` Entity.
|
||||
|
||||
:::info Deployment
|
||||
|
||||
1. Create the entity`otmoic.reputation` in the TerminusDID contract.
|
||||
2. Define a Tag named complaints.
|
||||
3. Deploy Otmoic Trader Reputation [contract](https://optimistic.etherscan.io/address/0xE924F7f68D1dcd004720e107F62c6303aF271ed3)
|
||||
4. Set the Tagger of complaints to Otmoic Trader Reputation.
|
||||
:::
|
||||
|
||||
:::info Usage
|
||||
|
||||
1. Use DID owner to sign the transaction info following EIP712
|
||||
2. Submit transaction info and signature to Otmoic Trader Reputation
|
||||
:::
|
||||
|
||||
:::info Structure
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
|
||||
otmoic{{OtmoicReputation}}
|
||||
did{{TerminusDID}}
|
||||
complaints[/tag-complaints/]
|
||||
otmoicdid((otmoic.reputation))
|
||||
|
||||
otmoic-- Authentication -->did
|
||||
did-.->otmoicdid
|
||||
otmoicdid-.-complaints
|
||||
otmoic-- read/write -->complaints
|
||||
```
|
||||
:::
|
||||
|
||||
## Application Reputation
|
||||
|
||||
1. Define the Tag named `ratings` and set Tagger as the Application Reputation contract
|
||||
2. When there is a new version for an app, create **`<version>.<appname>.app.myterminus.com`** on the blockchain.
|
||||
3. Users sign the comment/ rating info, and submit to the Application Reputation contract.
|
||||
4. The Application Reputation contract saves the rating info into Ratings of `<version>.<appname>.app.myterminus.com`, and publish the comment info in the form of event
|
||||
|
||||
:::info
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
|
||||
app((app.myterminus.com))
|
||||
appname((appname.app.myterminus.com))
|
||||
version1((version1.appname.app.myterminus.com))
|
||||
version2((version2.appname.app.myterminus.com))
|
||||
reputation{{TerminusAppMarketReputation}}
|
||||
ratings[/tag-ratings/]
|
||||
version1ratings[/version1-tag-ratings/]
|
||||
event[[event]]
|
||||
|
||||
app-->appname
|
||||
appname-->version1
|
||||
appname-->version2
|
||||
app-.->ratings
|
||||
ratings-.-version1ratings
|
||||
version1-.-version1ratings
|
||||
reputation-.- read/write -.->version1ratings
|
||||
reputation-.->event
|
||||
|
||||
```
|
||||
|
||||
:::
|
||||
278
docs/developer/contribute/olares-id/contract/did/design.md
Normal file
278
docs/developer/contribute/olares-id/contract/did/design.md
Normal file
@@ -0,0 +1,278 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
---
|
||||
|
||||
# DID
|
||||
|
||||
## Design of DID
|
||||
|
||||
### Data Contained in DIDs
|
||||
|
||||
#### id
|
||||
|
||||
Each DID is also a NFT (following **ERC-721** standard) and `id` is its unique identifier calculated by `keccak256(name)`.
|
||||
|
||||
#### did
|
||||
Derived from mnemonic phrases, used for authentication in systems relying on DIDs.
|
||||
|
||||
#### owner
|
||||
|
||||
Also derived from mnemonic phrases (following **BIP-44** standard) and used for authentication (mostly on-chain contracts).
|
||||
|
||||
#### note
|
||||
|
||||
DIDs have the following kinds for now:
|
||||
|
||||
- Individual
|
||||
- Organization
|
||||
- Entity
|
||||
|
||||
:::info Naming rules of TName
|
||||
A valid label of TName, i.e. each part separated by `.`:
|
||||
|
||||
- is a well-formed non-empty UTF-8 sequence; and
|
||||
- contains only Unicode code points in category L, M, N, P, S; and
|
||||
- does not contain Unicode code points in the following ranges:
|
||||
- Full Stop (U+002E)
|
||||
- Mongolian Free Variation Selectors (U+180B..U+180D)
|
||||
- Variation Selectors (U+FE00..U+FE0F)
|
||||
- Replacement Characters (U+FFFC..U+FFFD)
|
||||
- Variation Selectors Supplement (U+E0100..U+E01EF).
|
||||
- is case-sensitive, e.g. `MAX.com` and `max.com` are different TNames.
|
||||
- supports multiple characters including Chinese, e.g. `博物馆.中国`
|
||||
Ref: https://www.compart.com/en/unicode/category
|
||||
:::
|
||||
|
||||
|
||||
### Tag System
|
||||
|
||||
In addition to basic data, we hope to maintain some states or information in DIDs. That's why we implemented the tag system.
|
||||
|
||||
#### how to use
|
||||
|
||||
1. Define a tag
|
||||
2. Write to the tag
|
||||
|
||||
> [!NOTE]
|
||||
> The definition and structure of tags are complicated. Refer to the technical details of the contract if you are interested.
|
||||
> Here we briefly introduce the information in a tag definition.
|
||||
>
|
||||
> - name: the name of this tag
|
||||
> - did: the DID that defines this tag
|
||||
> - abiType: the data type of this tag, which follows the Solidity ABI specification and supports complicated structures
|
||||
> - fieldNames: the field names of structs inside this tag, flatten as a 2D string array using pre-order traversal
|
||||
|
||||
#### the scope of tags
|
||||
|
||||
Every tag is only applicable to the DID under which it was defined and its subdomains.
|
||||
For example, if the tag `employeeId` was defined in DID `a-certain-company.com`. Its subdomain `james.a-certain-company.com` can set `employeeId` value to `"001"` for itself. If the DIDs who have neither defined the tag nor is a subdomain of the definer, e.g. `another-company.com` and `emma.another-company.com`, cannot set the tag value.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
|
||||
a-certain((a-certain-company.com))
|
||||
another-company((another-company.com))
|
||||
emma((emma.another-company.com))
|
||||
james((james.a-certain-company.com))
|
||||
olivia((olivia.a-certain-company.com))
|
||||
employee-id[/employee-id/]
|
||||
|
||||
a-certain-->james
|
||||
a-certain-->olivia
|
||||
another-company-->emma
|
||||
a-certain-.-employee-id
|
||||
employee-id-.->james
|
||||
employee-id-.->olivia
|
||||
```
|
||||
|
||||
#### taggers
|
||||
|
||||
The tagger is part of necessary information in each tag. Unlike the fixed definition, taggers can be changed.
|
||||
A tagger represents the only person or program that has the authority to modify the tag value. It can be a wallet address or smart contract.
|
||||
|
||||
> [!NOTE]
|
||||
> We recommend you to use smart contracts for taggers.
|
||||
>
|
||||
> - You can utilize the Terminus DID contract for complete operator authentications, while achieving more fine-grained custom access control. Please refer to some official tagger implementations.
|
||||
> - For complicated tag structures or data with special formats, using contract taggers supports verifying data format on chain and customizing the rules. For example, in the official tagger we will verify the bytes data in PKCS8 ASN.1 format on chain to prevent accidentally setting unparsable values for the `RSAPubKey` tag.
|
||||
|
||||
### Who to Create DIDs
|
||||
|
||||
Since not everyone has enough gas for on-chain operations and is willing to use the owner address of existing DIDs, we offer the choice of sending tx by official forwarders on your behalf. Of couse you can also operate on your own if willing.
|
||||
|
||||
### Priviledges of DIDs
|
||||
|
||||
A DID has management priviledge over itself and all its subdomains. For 3rd-level domains which is registered directly, e.g. `a.b.c` without the registration of `b.c`, the owner of `b.c` will be set to `0x0...00d1d` and it is managed by the official.
|
||||
|
||||
### Introduction to processes and concepts
|
||||
|
||||
The extended information of TNames is managed by the tag system of the Terminus DID contract. Every tag has not only a name but also a tag type. For now, supported types include `int`, `uint`, `bool`, `string`, `address`, `bytes`, `bytesN`, `array`, `arrayN` and `tuple`. Tuples supports additional field names.
|
||||
Since tags are bound to TNames, the steps for setting a tag are:
|
||||
|
||||
- Define a tag in a TName with the tag name, tag type and field names in tuples. Setting the field names is not trivial so refer to the documentation "DIDv2.3Tag 类型注册流程介绍" for details.
|
||||
|
||||
Code example
|
||||
|
||||
```Solidity
|
||||
function defineTag(
|
||||
string calldata domain,
|
||||
string calldata name,
|
||||
bytes calldata abiType,
|
||||
string[][] calldata fieldNames
|
||||
) public
|
||||
```
|
||||
|
||||
- Set the tagger which is responsible for setting this tag. The tagger can be EOA or a contract address. Complicated validation logic can be implemented in contract taggers.
|
||||
|
||||
Code example
|
||||
|
||||
```Solidity
|
||||
function setTagger(string calldata domain, string calldata name, address tagger) public
|
||||
```
|
||||
|
||||
- Next you can perform CRUD of this tag on the definer and its subdomains.
|
||||
|
||||
```Solidity
|
||||
function addTag(string calldata from, string calldata to, string calldata name, bytes calldata value) public
|
||||
function removeTag(string calldata from, string calldata to, string calldata name) public
|
||||
function getTagElem(string calldata from, string calldata to, string calldata name, uint256[] calldata elemPath)
|
||||
public
|
||||
view
|
||||
returns (bytes memory)
|
||||
function updateTagElem(
|
||||
string calldata from,
|
||||
string calldata to,
|
||||
string calldata name,
|
||||
uint256[] calldata elemPath,
|
||||
bytes calldata value
|
||||
) public
|
||||
function getTagElemLength(
|
||||
string calldata from,
|
||||
string calldata to,
|
||||
string calldata name,
|
||||
uint256[] calldata elemPath
|
||||
) public view returns (uint256)
|
||||
function pushTagElem(
|
||||
string calldata from,
|
||||
string calldata to,
|
||||
string calldata name,
|
||||
uint256[] calldata elemPath,
|
||||
bytes calldata value
|
||||
) public
|
||||
function popTagElem(string calldata from, string calldata to, string calldata name, uint256[] calldata elemPath) public
|
||||
```
|
||||
|
||||
Among these
|
||||
|
||||
- `from` is the TName that defines this tag
|
||||
- `to` is the TName to set this tag
|
||||
- `name` is the tag name
|
||||
- `value` is the bytes value after `abi.encode`
|
||||
- `elemPath` is provided for `array` and `tuple` to access a single element. For other types just set it to an empty array. Its value is treated like indices of multi-dimensional arrays (think `tuple`s as arrays too). For example, the following type `Student` has 4-level nested tuples, and we can set `elemPath` to `[1,2,1,0,0]` to read or update `Student s -> Class class -> Teacher[1] teachers -> People info -> string name`. Note that we only have 4-level nested tuples but `elemPath` has length 5 because `teachers` is an array and adds another nesting level.
|
||||
|
||||
```Solidity
|
||||
struct Student {
|
||||
People info;
|
||||
Class class;
|
||||
}
|
||||
|
||||
struct Class {
|
||||
uint8 grade;
|
||||
uint8 classNum;
|
||||
Teacher[] teachers;
|
||||
}
|
||||
|
||||
struct People {
|
||||
string name;
|
||||
uint8 age;
|
||||
string gender;
|
||||
}
|
||||
|
||||
struct Teacher {
|
||||
People info;
|
||||
string subject;
|
||||
}
|
||||
Student s;
|
||||
```
|
||||
|
||||
- The Terminus DID contract provides a set of special tags, known as the official tags. It is defined in the empty TName `""` and its tagger is specified as the `RootTagger`. All TNames can set official tags and we'll introduce the detailed access control policy later.
|
||||
|
||||
### Access Control of Tags
|
||||
|
||||
1. Defining tags in a TName, i.e. setting the tag name and type
|
||||
|
||||
- Official tags: the `operator` has permission
|
||||
- Other tags: the owner of this TName has permission
|
||||
Defining tags can fail in the following situations:
|
||||
- A tag name can only be defined once in one TName, no duplicates
|
||||
- Tag names and field names of tuples must start with [a-z] and only contain [a-zA-Z0-9]
|
||||
- The bytes representation of a tag type cannot exceed length 31
|
||||
- The tag type must follow the type constraints in the `ABI` library
|
||||
- The value of `fieldNames` must match tuple types in the definition
|
||||
- A tuple cannot have duplicate field names
|
||||
|
||||
2. Setting the tagger:
|
||||
|
||||
- Official tags: the `operator` has permission
|
||||
- Other tags: the owner of this TName has permission
|
||||
|
||||
3. Setting the tag:
|
||||
|
||||
Taggers are responsible for access control of setting tags. Next we introduce the cases of official tags.
|
||||
|
||||
## Registration Rules and Access Control
|
||||
|
||||
From the contract's perspective, for now only the operator can register top-level TName. Normal registration is done by calling the `register` interface with arguments including the owner of the TName and some metadata. The metadata is immutable after registration. Sub-TNames can be registered by the operator or the owner of one of its parent TNames.
|
||||
That said, registration can only be performed by the following roles:
|
||||
|
||||
- The `operator` of the contract
|
||||
- The owner of a parent TName can register a sub-TName
|
||||
Registration can fail in the following cases:
|
||||
- when TName is not top-level: the parent TName is not registered yet or the parent's metadata specifies it cannot have sub-TNames
|
||||
- the TName contains an invalid label
|
||||
- the TName is already registered
|
||||
- the owner of the TName is specified as zero address
|
||||
|
||||
**Code example**:
|
||||
|
||||
```Solidity
|
||||
struct Metadata {
|
||||
string domain;
|
||||
string did;
|
||||
string notes;
|
||||
bool allowSubdomain;
|
||||
}
|
||||
|
||||
function register(address tokenOwner, Metadata calldata metadata) public returns (uint256 tokenId)
|
||||
```
|
||||
|
||||
After registration, the contract will mint a ERC-721 NFT with a returned token ID (`tokenId`). It is the Keccak-256 hash of the TName string.
|
||||
|
||||
**Code example**:
|
||||
|
||||
```Solidity
|
||||
function tokenId(string memory domain) internal pure returns (uint256) {
|
||||
return uint256(keccak256(bytes(domain)));
|
||||
}
|
||||
```
|
||||
|
||||
Since the NFT is ERC721-compatible, it supports standard ERC-721 operations like `transferFrom` and `approve`. So the ownership can be transferred by these addresses:
|
||||
|
||||
- the owner of the TName
|
||||
- the delegator of the owner
|
||||
- addresses approved by the owner
|
||||
|
||||
The Terminus DID contract adds two addresses that have transfer privilege:
|
||||
|
||||
- `operator` of the contract
|
||||
- the owner of the parent TName
|
||||
|
||||
## Roles of the Contract
|
||||
|
||||
The Terminus DID contract has an owner for upgrading itself. It also has a superuser, i.e. the `operator`, to facilitate configuration. Every TName has its owner.
|
||||
|
||||
The privileges of the contract owner include:
|
||||
|
||||
- setting the operator address
|
||||
- transferring ownership of the contract
|
||||
- upgrading the contract
|
||||
11
docs/developer/contribute/olares-id/contract/did/faq.md
Normal file
11
docs/developer/contribute/olares-id/contract/did/faq.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# FAQ
|
||||
|
||||
## Transaction Fee
|
||||
|
||||
When you apply for an Olares ID, we can afford the transaction fee for calling the contract. The rough process is:
|
||||
|
||||
- You provide us the data and signature signed by your private key
|
||||
- We send the transaction to the smart contract
|
||||
- The blockchain records your data after signature verification
|
||||
|
||||
If you need to manage data under your domain in the future, you should call the contract via MetaMask. Alternatively, you can log in to Olares Space using LarePass and pay the the transaction fee by yourself.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Official Taggers
|
||||
|
||||
## RootTagger
|
||||
|
||||
So far we have defined the following tags and use `RootTagger` as their taggers:
|
||||
|
||||
- rsaPubKey: the RSA public key of a TName
|
||||
type: `bytes`; access: the operator, the owner of the TName, and the owner of a parent TName
|
||||
- dnsARecord: the IP address of a TName
|
||||
type: `bytes4`; access: the operator, the owner of the TName, and the owner of a parent TName
|
||||
- latestDID: the latest DID of a TName (this is added because the metadata is immutable)
|
||||
type: `string`; access: the operator, the owner of the TName, and the owner of a parent TName
|
||||
- authAddresses: the addresses controlled by the owner of a TName
|
||||
type: `tuple(uint8,address)[]`; access: anyone with EIP-712 signatures of the owner of the TName and the added address
|
||||
|
||||
## AppStoreReputation
|
||||
|
||||
There is another special tagger `AppStoreReputation` for the tag `ratings` with type `tuple(string,uint8)[]` defined in the TName `app.myterminus.com`. It provides on-chain storage for ratings of apps in Terminus OS. Each app has a corresponding sub-TName `<appVersion>.<appId>.app.myterminus.com` where anyone who has a TName can submit ratings for these apps.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Release History
|
||||
|
||||
## V2.0.0
|
||||
|
||||
Implement core architecture of DID module in Terminus DID
|
||||
|
||||
## V2.1.0
|
||||
|
||||
Add tag system without definition
|
||||
|
||||
## V2.2.0
|
||||
|
||||
Upgrade tag system to include type definition
|
||||
|
||||
## V2.3.0
|
||||
|
||||
Optimize contract structure and reduce core contract size
|
||||
|
||||
## V2.3.1
|
||||
|
||||
Bug-fix: Revert instead of going on with zero address when EIP-712 signature recover fails
|
||||
104
docs/developer/contribute/olares-id/contract/manage/contract.md
Normal file
104
docs/developer/contribute/olares-id/contract/manage/contract.md
Normal file
@@ -0,0 +1,104 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
---
|
||||
|
||||
# Call the contract directly
|
||||
|
||||
## DID
|
||||
|
||||
### Get metadata
|
||||
|
||||
There are two interfaces for fetching the metadata of a DID - `getMetadata(tokenId)` and `getMetadata(name)`.
|
||||
|
||||
#### Return value
|
||||
|
||||
```json
|
||||
[
|
||||
"james.myterminus.com", // name
|
||||
"did:key:z6MkpLwxcTwhj4MRm4eKhvBadK45qHr5QEYHUXNyhCfkXJ9U#z6MkpLwxcTwhj4MRm4eKhvBadK45qHr5QEYHUXNyhCfkXJ9U", // DID derived from mnemonic phrases
|
||||
"OrganizationalUser", // DID type
|
||||
true // allowed to create subdomains?
|
||||
]
|
||||
```
|
||||
|
||||
### Get the owner of a domain
|
||||
|
||||
Call `ownerOf(tokenId)` to get the on-chain controller address of a DID.
|
||||
|
||||
### Get token by index of creation
|
||||
|
||||
Call `tokenByIndex(index)` to get the token with a specified index. It returns a **token ID**.
|
||||
|
||||
### Get token by owner and index
|
||||
|
||||
Although we disallow owning multiple DIDs by a single wallet from the business' perspective, the contract allows this considering possible ownership transferring and NFT trading in the future. In this case, `tokenOfOwnerByIndex(owner, index)` can be called to get the token owned by a specified address with a specified index. It returns a **token ID**.
|
||||
|
||||
### Register DID
|
||||
|
||||
The owner of a domain can call `register(owner, MetaData(domain, did, note, allowSubdomain))` to register its subdomains.
|
||||
|
||||
> [!NOTE]
|
||||
> The first parameter **owner** is the specified owner of the new DID and the second parameter is a struct of metadata containing.
|
||||
>
|
||||
> - domain: the complete domain name of the new DID, which is also a Olares ID
|
||||
> - did: the DID derived from the owner's wallet
|
||||
> - note: notes about the new DID, used by off-chain systems for categorization
|
||||
> - allowSubdomain: whether to allow the new DID to register subdomains
|
||||
>
|
||||
> The metadata cannot be changed after registration. If the ownership is transferred in the future, the new DID record will be written to the `latestDid` tag.
|
||||
|
||||
## Tag
|
||||
|
||||
### Get the number of tags defined by a name
|
||||
|
||||
`getDefinedTagCount(name)` returns how many tags are defined by a specified TName.
|
||||
|
||||
### Get tag name
|
||||
|
||||
Used with the above interface, `getDefinedTagNameByIndex(name, index)` returns a single tag name and `getDefinedTagNames(name)` returns all tag names defined by a specified TName.
|
||||
|
||||
### Structured tag
|
||||
|
||||
If a tag type is a complicated structure instead of primitive value, call `getTagType(name, tagName)` to query the structure definition and then call `getFieldNamesEventBlock(fieldNamesHash)` with previously returned **fieldNamesHash** to get the block number at which this tag is defined. Finally, use the `ethers` library to get field names in the definition.
|
||||
|
||||
> [!NOTE]
|
||||
> The interface for querying tag type returns an encoded bytes of the ABI type, which should be parsed according to the code table. Querying field names can be complicated and error-prone, so we recommend to use functions in the SDK to fetch data about tags instead of calling the contract manually.
|
||||
|
||||
Call `getTagElem(definedDidName, valueDidName, tagName, elemPath)` to get the value of a tag after getting the tag type.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> - definedDidName: the TName defining this tag
|
||||
> - valueDidName: the TName whose tag value is desired
|
||||
> - elemPath: path selector to read an inner element for tuples or arrays (`[]` for reading the full tag)
|
||||
>
|
||||
> This interface returns ABI-encoded data which should be parsed according to the tag type.
|
||||
> It is also recommended to use SDK instead of calling the contract manually.
|
||||
|
||||
### Define a tag
|
||||
|
||||
Call `defineTag(didName, tagName, abiType, fieldNames)` with the owner of `didName` to define a tag.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> - abiType: data type of this tag following our **ABI** code format, which supports complicated structures
|
||||
> - fieldNames: the field names of structs inside this tag, flatten as a 2D string array using pre-order traversal
|
||||
|
||||
### CRUD
|
||||
|
||||
> [!NOTE]
|
||||
> We recommend to interact with the tagger contract instead of using the following interfaces to perform tag operations.
|
||||
|
||||
- Create - Call `addTag(defineDidName, valueDidName, tagName, value)` to add a tag.
|
||||
|
||||
- Update - Call `updateTagElem(defineDidName, valueDidName, tagName, elemPath, value)` to update a piece of data in a tag.
|
||||
|
||||
- Delete - Call `removeTag(defineDidName, valueDidName, tagName)` to delete a tag. This deletes a tag set on a DID instead of deleting the tag definition.
|
||||
|
||||
- Array operations - If the tag type contains an array, call `popTagElem(defineDidName, valueDidName, tagName, elePaths)` and `pushTagElem(defineDidName, valueDidName, tagName, elePaths, value)` to perform array-specific operations.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> - defineDidName: the TName defining this tag
|
||||
> - valueDidName: the TName on which the tag value is set
|
||||
> - value: ABI-encoded tag value
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
---
|
||||
|
||||
# Environment
|
||||
|
||||
## Sepolia Optimistic
|
||||
|
||||
- TerminusDID: 0x4c8c98e652d6a01494971a8faF5d3b68338f9ED4
|
||||
- RootTagger: 0xaA5bE49799b6A71Eda74d22D01F7A808aFf41b3f
|
||||
- LibABI: 0xdc9e8faDe38eE9E2Eb43761f1553CD2360ecAEac
|
||||
|
||||
## Optimistic
|
||||
|
||||
- TerminusDID: 0x5da4fa8e567d86e52ef8da860de1be8f54cae97d
|
||||
- RootTagger: 0xe2eaba0979277a90511f8873ae1e8ca26b54e740
|
||||
- LibABI: 0x9ae3f16bd99294af1784beb1a0a5c84bf2636365
|
||||
|
||||
> [!NOTE]
|
||||
> RootTagger is the tagger for the following official tags: - DNSARecord - RSAPubKey - LatestDid - AuthAddress
|
||||
|
||||
> [!NOTE]
|
||||
> LibABI is an independent library contract, providing functions like ABI type encoding, data conversion etc.
|
||||
299
docs/developer/contribute/olares-id/contract/manage/sdk.md
Normal file
299
docs/developer/contribute/olares-id/contract/manage/sdk.md
Normal file
@@ -0,0 +1,299 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
---
|
||||
|
||||
# Management with SDK
|
||||
|
||||
## DID
|
||||
|
||||
### Get all DIDs
|
||||
|
||||
Sometimes we need complete data for statistical or other reasons. The following `fetchAll` method utilizes the contract interfaces to get complete data. Although it gets the result directly from on-chain interfaces instead of traversing Ethereum events, this can be time-consuming as the amount of data is large.
|
||||
|
||||
```Typescript
|
||||
import DID from 'did-contract-developer-components'
|
||||
|
||||
const RPC = "you-rpc-url"
|
||||
const CONTRACT_DID = "0x5da4fa8e567d86e52ef8da860de1be8f54cae97d"
|
||||
const CONTRACT_ROOT_RESOLVER = "0xe2eaba0979277a90511f8873ae1e8ca26b54e740"
|
||||
const CONTRACT_ABI_TYPE = "0x9ae3f16bd99294af1784beb1a0a5c84bf2636365"
|
||||
|
||||
const fetch = async () => {
|
||||
const did = DID.createConsole(RPC, CONTRACT_DID, CONTRACT_ROOT_RESOLVER, CONTRACT_ABI_TYPE)
|
||||
|
||||
const dids = await did.fetchAll()
|
||||
|
||||
console.log('dids:', dids)
|
||||
|
||||
console.log('format dids:', await did.formatDatas(dids))
|
||||
}
|
||||
|
||||
fetch()
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> If running in development environment and logging is on, the fetching progress is shown during execution.
|
||||
|
||||
#### Fast fetch
|
||||
|
||||
For your convenience, the SDK provides two functions `formatDatas` and `loadDatas`. The data returned by `formatDatas` can be stored and it can be loaded by `loadDatas` next time to reduce syncing duration. A simpler way is to access the `/all` endpoint in the official **did-support** service, which can also be loaded by `loadDatas`.
|
||||
|
||||
### Query specific DID
|
||||
|
||||
After reading the above contract interfaces, you should notice that the contract does not provide a single interface for fetching complete data related to a DID. So we simplified the design of interfaces in the SDK, where `fetchDomain` returns complete data for a DID.
|
||||
|
||||
```Typescript
|
||||
import DID from 'did-contract-developer-components'
|
||||
|
||||
const RPC = "you-rpc-url"
|
||||
const CONTRACT_DID = "0x5da4fa8e567d86e52ef8da860de1be8f54cae97d"
|
||||
const CONTRACT_ROOT_RESOLVER = "0xe2eaba0979277a90511f8873ae1e8ca26b54e740"
|
||||
const CONTRACT_ABI_TYPE = "0x9ae3f16bd99294af1784beb1a0a5c84bf2636365"
|
||||
|
||||
const fetch = async () => {
|
||||
const did = DID.createConsole(RPC, CONTRACT_DID, CONTRACT_ROOT_RESOLVER, CONTRACT_ABI_TYPE)
|
||||
|
||||
const domain = await did.fetchDomain('james.myterminus.com')
|
||||
|
||||
console.log('did:', domain)
|
||||
}
|
||||
|
||||
fetch()
|
||||
```
|
||||
|
||||
#### Fast query
|
||||
|
||||
After you execute `loadDatas` in the above SDK examples, `fetchDomain` will first try to match local data and only fetch data on-chain if there is no data for this DID on local machine.
|
||||
|
||||
#### Update DID
|
||||
|
||||
If you are worried that fast query does not return the latest data, use `updateDomain` or `updateDomainById` to update local data.
|
||||
|
||||
#### Fuzzy matching
|
||||
|
||||
Like fast query, you can use the fuzzy matching function in SDK after loading data locally. The following two functions only match the local data:
|
||||
|
||||
```Typescript
|
||||
import DID from 'did-contract-developer-components'
|
||||
//... code
|
||||
|
||||
// Query DID by owner and return all subdomains
|
||||
const domainsByOwner = DID.Domain.findSubtreesByOwner(owner, did.treesCache)
|
||||
|
||||
//Query DID by did and return all subdomains
|
||||
const domainsByDid = DID.Domain.findSubtreesByDid(did, did.treesCache)
|
||||
|
||||
```
|
||||
|
||||
## Tag
|
||||
|
||||
### Get all tags of a DID
|
||||
|
||||
Unlike calling the contract directly, this function returns data with parsed tag structure and struct field names.
|
||||
|
||||
```Typescript
|
||||
import DID from 'did-contract-developer-components'
|
||||
|
||||
const RPC = "you-rpc-url"
|
||||
const CONTRACT_DID = "0x5da4fa8e567d86e52ef8da860de1be8f54cae97d"
|
||||
const CONTRACT_ROOT_RESOLVER = "0xe2eaba0979277a90511f8873ae1e8ca26b54e740"
|
||||
const CONTRACT_ABI_TYPE = "0x9ae3f16bd99294af1784beb1a0a5c84bf2636365"
|
||||
|
||||
const fetch = async () => {
|
||||
const did = DID.createConsole(RPC, CONTRACT_DID, CONTRACT_ROOT_RESOLVER, CONTRACT_ABI_TYPE)
|
||||
|
||||
const domain = await did.fetchDomain('james.myterminus.com')
|
||||
|
||||
if (domain == undefined) {
|
||||
throw new Error("not found");
|
||||
}
|
||||
|
||||
const tags = await DID.Domain.fetchAllTagType(domain, did.getContractDID())
|
||||
|
||||
console.log('tags:', tags)
|
||||
}
|
||||
|
||||
fetch()
|
||||
```
|
||||
|
||||
### Get all tag values of a DID
|
||||
|
||||
Like the above method, this function returns parsed data with JSON tag values.
|
||||
|
||||
```Typescript
|
||||
import DID from 'did-contract-developer-components'
|
||||
|
||||
const RPC = "you-rpc-url"
|
||||
const CONTRACT_DID = "0x5da4fa8e567d86e52ef8da860de1be8f54cae97d"
|
||||
const CONTRACT_ROOT_RESOLVER = "0xe2eaba0979277a90511f8873ae1e8ca26b54e740"
|
||||
const CONTRACT_ABI_TYPE = "0x9ae3f16bd99294af1784beb1a0a5c84bf2636365"
|
||||
|
||||
const fetch = async () => {
|
||||
const did = DID.createConsole(RPC, CONTRACT_DID, CONTRACT_ROOT_RESOLVER, CONTRACT_ABI_TYPE)
|
||||
|
||||
const domain = await did.fetchDomain('james.myterminus.com')
|
||||
|
||||
if (domain == undefined) {
|
||||
throw new Error("not found");
|
||||
}
|
||||
|
||||
await DID.Domain.fetchAllTagType(domain, did.getContractDID())
|
||||
|
||||
const tags = await DID.Domain.fetchAllTagValue(domain, did.getContractDID())
|
||||
|
||||
console.dir(tags, {depth: null});
|
||||
}
|
||||
|
||||
fetch()
|
||||
```
|
||||
|
||||
### Define tag
|
||||
|
||||
Using SDK to define a tag, you can construct the inner structure of the tag type with an object-oriented approach without worrying about the encoding. The following examples include most common data types.
|
||||
|
||||
```Typescript
|
||||
import DID from 'did-contract-developer-components'
|
||||
|
||||
const RPC = "you-rpc-url"
|
||||
const CONTRACT_DID = "0x5da4fa8e567d86e52ef8da860de1be8f54cae97d"
|
||||
const CONTRACT_ROOT_RESOLVER = "0xe2eaba0979277a90511f8873ae1e8ca26b54e740"
|
||||
const CONTRACT_ABI_TYPE = "0x9ae3f16bd99294af1784beb1a0a5c84bf2636365"
|
||||
|
||||
const defineTag = async () => {
|
||||
|
||||
// tag name: simpleTag
|
||||
const tagName = 'simpleTagBox'
|
||||
|
||||
// tuple
|
||||
const testTuple = new DID.Tag.TagValueTypeTuple(undefined, undefined, true);
|
||||
|
||||
// uint
|
||||
const testUint = new DID.Tag.TagValueTypeUint(undefined, undefined, true);
|
||||
testUint.setSize(8)
|
||||
|
||||
// address
|
||||
const testAddress = new DID.Tag.TagValueTypeAddress(undefined, undefined, true);
|
||||
|
||||
// array<address>
|
||||
const testArrayAddress = new DID.Tag.TagValueTypeArray(undefined, undefined, true);
|
||||
testArrayAddress.setBuliderType(testAddress)
|
||||
|
||||
// bool
|
||||
const testBool= new DID.Tag.TagValueTypeBool(undefined, undefined, true)
|
||||
|
||||
// bytes
|
||||
const testBytes = new DID.Tag.TagValueTypeBytes(undefined, undefined, true)
|
||||
|
||||
// int
|
||||
const testInt = new DID.Tag.TagValueTypeInt(undefined, undefined, true)
|
||||
testInt.setSize(256)
|
||||
|
||||
// flarray<int>
|
||||
const testFlarrayInt = new DID.Tag.TagValueTypeFlarray(undefined, undefined, true)
|
||||
testFlarrayInt.setBuliderType(testInt)
|
||||
testFlarrayInt.setSize(3)
|
||||
|
||||
// flbytes
|
||||
const testFlbytes = new DID.Tag.TagValueTypeFlbytes(undefined, undefined, true)
|
||||
testFlbytes.setSize(5)
|
||||
|
||||
// string
|
||||
const testString = new DID.Tag.TagValueTypeString(undefined, undefined, true)
|
||||
|
||||
testTuple.setField('testUint', testUint)
|
||||
testTuple.setField('testArrayAddress', testArrayAddress)
|
||||
testTuple.setField('testBool', testBool)
|
||||
testTuple.setField('testBytes', testBytes)
|
||||
testTuple.setField('testFlarrayInt', testFlarrayInt)
|
||||
testTuple.setField('testFlbytes', testFlbytes)
|
||||
testTuple.setField('testString', testString)
|
||||
|
||||
const did = DID.createConsole(RPC, CONTRACT_DID, CONTRACT_ROOT_RESOLVER, CONTRACT_ABI_TYPE)
|
||||
|
||||
const domain = await did.fetchDomain('james.myterminus.com')
|
||||
|
||||
await DID.Domain.defineTag(domain, tagName, testTuple, did.getContractDIDByPrivateKey('you-private-key'), did)
|
||||
}
|
||||
|
||||
defineTag()
|
||||
```
|
||||
|
||||
### Set tagger
|
||||
|
||||
```Typescript
|
||||
import DID from 'did-contract-developer-components'
|
||||
|
||||
const RPC = "you-rpc-url"
|
||||
const CONTRACT_DID = "0x5da4fa8e567d86e52ef8da860de1be8f54cae97d"
|
||||
const CONTRACT_ROOT_RESOLVER = "0xe2eaba0979277a90511f8873ae1e8ca26b54e740"
|
||||
const CONTRACT_ABI_TYPE = "0x9ae3f16bd99294af1784beb1a0a5c84bf2636365"
|
||||
|
||||
const setTagger = async () => {
|
||||
|
||||
const did = DID.createConsole(RPC, CONTRACT_DID, CONTRACT_ROOT_RESOLVER, CONTRACT_ABI_TYPE)
|
||||
|
||||
const domain = await did.fetchDomain('james.myterminus.com')
|
||||
|
||||
if (domain == undefined) {
|
||||
throw new Error("not found");
|
||||
}
|
||||
|
||||
const [tag] = domain.tags.filter(tag => tag.name == 'simpleTagBox')
|
||||
|
||||
DID.Domain.setTagger(tag, 'you-tagger-address', did.getContractDIDByPrivateKey('you-private-key'))
|
||||
}
|
||||
|
||||
setTagger()
|
||||
```
|
||||
|
||||
### Set tag value
|
||||
|
||||
Like defining a tag above, when using SDK to set tag value, you only need to pass in suitable JSON data without worrying about the encoding. The tag types in the following examples are the same as the tag definition section above.
|
||||
|
||||
```Typescript
|
||||
import DID from 'did-contract-developer-components'
|
||||
|
||||
const RPC = "you-rpc-url"
|
||||
const CONTRACT_DID = "0x5da4fa8e567d86e52ef8da860de1be8f54cae97d"
|
||||
const CONTRACT_ROOT_RESOLVER = "0xe2eaba0979277a90511f8873ae1e8ca26b54e740"
|
||||
const CONTRACT_ABI_TYPE = "0x9ae3f16bd99294af1784beb1a0a5c84bf2636365"
|
||||
|
||||
const setTagValue = async () => {
|
||||
const did = DID.createConsole(RPC, CONTRACT_DID, CONTRACT_ROOT_RESOLVER, CONTRACT_ABI_TYPE)
|
||||
|
||||
const domain = await did.fetchDomain('james.myterminus.com')
|
||||
|
||||
if (domain == undefined) {
|
||||
throw new Error("not found");
|
||||
}
|
||||
|
||||
console.log(domain)
|
||||
const [tag] = domain.tags.filter(tag => tag.name == 'simpleTagBox')
|
||||
const tagType = await DID.Domain.fetchTagStructure(tag, did.getContractDID())
|
||||
console.dir(tagType, {depth: null})
|
||||
|
||||
const ba1 = ethers.hexlify(Uint8Array.from([1, 2, 3]))
|
||||
const ba2 = ethers.hexlify(Uint8Array.from([10, 18, 19]))
|
||||
|
||||
const newData = {
|
||||
testbox: {
|
||||
testUint: 1,
|
||||
testArrayAddress: ['0xF18B2Ea28c722CA87f951F5bF5327b66a7dd72A3', '0xecBA1d33b889f66ad426535f970d1E033ba5c79C'],
|
||||
testBool: true,
|
||||
testBytes: '0x0102030405' ,
|
||||
testFlarrayInt: [2, 3, 4],
|
||||
testFlbytes: '0x0a0b0c0d0e',
|
||||
testString: 'ok'
|
||||
}
|
||||
}
|
||||
console.log('newData', newData)
|
||||
|
||||
const call = await DID.Tag.doEncode(tagType, newData)
|
||||
console.log('call', call)
|
||||
|
||||
//set
|
||||
const resp = await DID.Domain.setValue(tag, call, did.getContractDIDByPrivateKey('you-private-key'))
|
||||
}
|
||||
|
||||
setTagValue()
|
||||
```
|
||||
26
docs/developer/contribute/olares-id/index.md
Normal file
26
docs/developer/contribute/olares-id/index.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
# https://vitepress.dev/reference/default-theme-home-page
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: "Snowinning Protocol"
|
||||
text: ""
|
||||
tagline: Building a Self-Sovereign Internet
|
||||
actions:
|
||||
- theme: brand
|
||||
text: What is Snowinning
|
||||
link: /protocol/overview.md
|
||||
- theme: alt
|
||||
text: Vist GitHub
|
||||
link: https://github.com/beclab/terminus
|
||||
|
||||
features:
|
||||
- title: Snowinning Protocol
|
||||
details: A decentralized identity and credit system that facilitates secure, trustless exchanges of information and value.
|
||||
- title: Smart Contract
|
||||
details: Snowinning Protocol's smart contract consists of DID Registry and the extended reputation systems based on DID.
|
||||
|
||||
- title: Verifiable Credential (VC)
|
||||
details: Fully ratified W3C standard that work together with DIDs to enable trustless, secure interactions.
|
||||
---
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
# Issuer
|
||||
|
||||

|
||||
|
||||
The following is the issuer process:
|
||||
|
||||
1. Holder gets the Manifest from Issuer.
|
||||
2. Holder signs it and submits the Application.
|
||||
3. Issuer reviews the Application automatically or manually.
|
||||
4. Holder receives the review results from the Issuer and either gets the VC if approved or a reason if rejected.
|
||||
|
||||
## Manifest
|
||||
|
||||
This file will be returned to Holder.
|
||||
`outputDescriptors` is given for wallets to display VCs.
|
||||
`presentationDefinition` is in fact the later `manifest_presentation` file, used to confirm the format of Application submitted by users.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Facebook Verifiable Credential Manifest",
|
||||
"description": "Facebook Verifiable Credential Manifest",
|
||||
"issuerDid": "",
|
||||
"issuerName": "",
|
||||
"outputDescriptors": [
|
||||
{
|
||||
"id": "",
|
||||
"schema": "",
|
||||
"name": "Facebook Verifiable Credential Manifest",
|
||||
"description": "Facebook Verifiable Credential Manifest",
|
||||
"display": {
|
||||
"title": {
|
||||
"path": ["$.credentialSubject.name", "$.vc.credentialSubject.name"],
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
"subtitle": {
|
||||
"path": ["$.credentialSubject.title", "$.vc.credentialSubject.title"],
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
"description": {
|
||||
"path": [
|
||||
"$.credentialSubject.description",
|
||||
"$.vc.credentialSubject.description"
|
||||
],
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"path": ["$.credentialSubject.id", "$.vc.credentialSubject.id"],
|
||||
"schema": { "type": "string" },
|
||||
"label": "ID"
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
"$.credentialSubject.email",
|
||||
"$.vc.credentialSubject.email"
|
||||
],
|
||||
"schema": { "type": "string" },
|
||||
"label": "Email"
|
||||
}
|
||||
]
|
||||
},
|
||||
"styles": {
|
||||
"background": {
|
||||
"color": "#FFFFFF"
|
||||
},
|
||||
"text": {
|
||||
"color": "#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"format": {
|
||||
"jwt_vc": {
|
||||
"alg": ["EdDSA"]
|
||||
}
|
||||
},
|
||||
"presentationDefinition": {}
|
||||
}
|
||||
```
|
||||
|
||||
## Manifest Presentation
|
||||
|
||||
Issuer uses this file to validate the format of the Application submitted by the Holder.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Facebook Manifest Presentation Definition",
|
||||
"purpose": "Provide your token required to Facebook",
|
||||
"inputDescriptors": [
|
||||
{
|
||||
"id": "token",
|
||||
"name": "Access Token",
|
||||
"purpose": "Provide your token required to Facebook",
|
||||
"format": {
|
||||
"jwt_vc": {
|
||||
"alg": ["EdDSA"]
|
||||
}
|
||||
},
|
||||
"constraints": {
|
||||
"fields": [
|
||||
{
|
||||
"path": ["$.credentialSubject.token"]
|
||||
}
|
||||
],
|
||||
"subject_is_issuer": "preferred"
|
||||
}
|
||||
}
|
||||
],
|
||||
"author": ""
|
||||
}
|
||||
```
|
||||
|
||||
## Application Verifiable Credential
|
||||
|
||||
Although users only need `manifest_presentation` to construct VC data in client, servers need a schema to verify whether `manifest_presentation` meets the format requirements.
|
||||
This file will be returned to Holder so that Holder knows the format of data required to submit for verification.
|
||||
In the scenarios of Facebook, Twitter and Gmail, users login in client and get the access token, then submit it to the Issuer server, which can get the basic information of the user with the access token from e.g. the Facebook server etc. So users only need to submit the token which is simple.
|
||||
But in the scenario of KYC, users need to submit full name, ID photo or even verification video etc., and the fields in this file can become much more complicated.
|
||||
|
||||
```json
|
||||
{
|
||||
"author": "",
|
||||
"name": "Facebook Verifiable Credential Request Schema",
|
||||
"schema": {
|
||||
"$id": "facebook-schema-1.0",
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "Facebook Verifiable Credential Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"token": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["token"],
|
||||
"additionalProperties": true
|
||||
},
|
||||
"sign": false
|
||||
}
|
||||
```
|
||||
|
||||
## Verifiable Credential
|
||||
|
||||
The VC format returned to Holder by Issuer
|
||||
|
||||
```json
|
||||
{
|
||||
"author": "",
|
||||
"name": "Facebook Verifiable Credential Schema",
|
||||
"schema": {
|
||||
"$id": "facebook-schema-1.0",
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "Facebook Verifiable Credential Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
"facebook_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"profile_image": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"facebook_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"picture_is_silhouette": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["name", "title", "description", "facebook_name"],
|
||||
"additionalProperties": true
|
||||
},
|
||||
"sign": false
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,46 @@
|
||||
# Use case with Olares
|
||||
|
||||
While DIDs solve the issue of identity in a decentralized network, they are typically difficult for humans to remember or use in daily situations. Olares ID provides a familiar, easy-to-remember format similar to email addresses, while still leveraging the power and security of DIDs. Each Olares ID is bound to a DID.
|
||||
|
||||
## Potential fairness issues
|
||||
|
||||
The **first-come, first-served** system in Olares ID registration may present some fairness issues, such as:
|
||||
|
||||
1. **Fraud:** For instance, the Olares ID `elonmusk@myterminus.com` might be registered by someone who isn't actually Elon Mask.
|
||||
2. **Speculation:** Pre-registration of popular names could lead to speculation, potentially boosting early network activity, but at the cost of fairness.
|
||||
|
||||
## VC process for Olares ID
|
||||
|
||||
To address the potential faireness concerns, we adopted principles from **Self-Sovereign Identity (SSI)** services proposed by the Web5 team, along with the [VC process](/manual/concepts/vc.md#verification-process) of Olares ID. This led us to design an **Issuer and Verifier** process to assist users in applying for a **Olares ID**.
|
||||
|
||||

|
||||
|
||||
### Gmail issuer service
|
||||
|
||||
We utilize Google's OAuth process to facilitate the issuance of **Verifiable Credentials (VCs)**. The simplified process is as follows:
|
||||
|
||||
1. Alice logs into her Gmail account via OAuth in LarePass, the wallet client.
|
||||
2. Google returns the OAuth credentials to LarePass.
|
||||
3. LarePass submits the OAuth credentials to the Issuer.
|
||||
4. The Issuer confirms the validity of the credentials with Google's servers and retrieves basic information (e.g., email name).
|
||||
5. The Issuer issues a VC to Alice that matches the local part of her Gmail address.
|
||||
|
||||
Alice can now store the issued VC in LarePass.
|
||||
|
||||
:::tip NOTE
|
||||
- Throughout the process, Alice only reveals basic account data within the scope of the credential authorization to LarePass and the Issuer service, with password and privacy protection ensured by Google's OAuth protocol.
|
||||
- All the code for setting up a Gmail Issuer Service or other Web2 service Issuer Services are open sourced on GitHub.
|
||||
:::
|
||||
|
||||
### Olares ID verifier service
|
||||
|
||||
Here's how the **Verifier Service** works on the Olares end:
|
||||
|
||||
1. Alice packages her DID, Olares ID, and Gmail VC into a **Verifiable Presentation (VP)** and submits the VP with its signature to the Verifier Service.
|
||||
2. The Verifier Service checks:
|
||||
- The signature's validity.
|
||||
- The validity of the VC in the VP.
|
||||
- Whether the Olares ID can be registered on the blockchain (conflicts may arise if multiple channels, such as Gmail and Twitter, are used for VC information).
|
||||
3. After all checks pass, the Verifier Service submits Alice's information to the blockchain and covers the Gas fees.
|
||||
|
||||
At this point, Alice successfully obtains her **Olares ID**. For example, if you apply with the Gmail address "hello@gmail.com", you'll receive the Olares ID "hello@myterminus.com" once all checks are completed.
|
||||
@@ -0,0 +1,55 @@
|
||||
# VC Service
|
||||
|
||||
The purpose of this documentation is to help you understand how to configure various schemas and create Issuers and Verifiers for your own scenarios using:
|
||||
- [verifiable-credential-gate](https://github.com/Above-Os/verifiable-credential-gate) and [did-gate](https://github.com/Above-Os/did-gate) by Terminus.
|
||||
- [SSI Service](https://github.com/TBD54566975/ssi-service) by the tbd team.
|
||||
|
||||
::: tip
|
||||
If you want to engage in lower-level development, read the [protocol standard](#reference) together with the source code in [SSI SDK](https://github.com/TBD54566975/ssi-sdk).
|
||||
:::
|
||||
|
||||
## Introduction
|
||||
|
||||
We have learned about [VC](/manual/concepts/vc.md) and the basic process for applying VCs.
|
||||
|
||||
Before we get into the implementation details, we can familiarize ourselves with the terms that we will encounter in the real communication process of Wallet, Verifier and Issuer.
|
||||
| Term | Definition |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Holder | Holders are entities that have one or more verifiable credentials in their possession. Holders are also the entities that submit proofs to Verifiers to satisfy the requirements described in a Presentation Definition. |
|
||||
| Issuer | A role an entity can perform by asserting claims about one or more subjects, creating a verifiable credential from these claims, and transmitting the verifiable credential to a holder. |
|
||||
| Verifier | Verifiers are entities that define what proofs they require from a Holder (via a Presentation Definition) in order to proceed with an interaction. |
|
||||
| Verifiable Credential | Is a tamper-evident credential that has authorship that can be cryptographically verified. Verifiable credentials can be used to build Verifiable Presentations, which can also be cryptographically verified. The claims in a credential can be about different subjects. PEX accepts Verifiable credential in 3 forms: 1. JSON_LD which is known in our system as IVerifiableCredential, 2. JWT-Wrapped VC which is known in our system as JwtWrappedVerifiableCredential or string which is a valid Verifiable credential jwt |
|
||||
| Verifiable Presentation | s a tamper-evident presentation encoded in such a way that authorship of the data can be trusted after a process of cryptographic verification. |
|
||||
| Manifest | Credential Manifests are used to describe which credentials are available for issuance. |
|
||||
| Application | The format provided by Holder to Issuer, including |
|
||||
| Presentation Definition | Presentation Definitions are objects that articulate what proofs a Verifier requires. |
|
||||
| Presentation | Data derived from one or more verifiable credentials, issued by one or more issuers |
|
||||
| Submission | TBC |
|
||||
| Definition | TBC |
|
||||
| Schema | All different Manifest, Application, Credential, Presentation, Definition need to define Schema with JSON. The service will verify the correctness of submitted data and then go into business process. |
|
||||
|
||||
## Reference
|
||||
|
||||
The following reference materials come from the [SSI SDK](https://github.com/TBD54566975/ssi-sdk) project.
|
||||
|
||||
### Specifications
|
||||
|
||||
Here are a set of references to specifications that this library currently supports. It is a dynamic set that will change as the library evolves.
|
||||
|
||||
- [Decentralized Identifiers (DIDs) v1.0](https://www.w3.org/TR/did-core/) W3C Proposed Recommendation 03 August 2021
|
||||
- [Verifiable Credentials Data Model v1.1](https://www.w3.org/TR/vc-data-model/) W3C Recommendation 09 November 2021
|
||||
- Supports [Linked Data Proof](https://www.w3.org/TR/vc-data-model/#data-integrity-proofs) formats.
|
||||
- Supports [VC-JWT and VP-JWT](https://www.w3.org/TR/vc-data-model/#json-web-token) formats.
|
||||
- [Verifiable Credentials JSON Schema Specification](https://w3c-ccg.github.io/vc-json-schemas/v2/index.html) Draft Community Group Report, 21 September 2021
|
||||
- [Presentation Exchange 2.0.0](https://identity.foundation/presentation-exchange/) Working Group Draft, March 2022
|
||||
- [Wallet Rendering Strawman](https://identity.foundation/wallet-rendering/), June 2022
|
||||
- [Credential Manifest](https://identity.foundation/credential-manifest/) Strawman, June 2022
|
||||
- [Status List 2021](https://w3c-ccg.github.io/vc-status-list-2021/) Draft Community Group Report 04 April 2022
|
||||
|
||||
### Signing Methods
|
||||
|
||||
> - [Data Integrity 1.0](https://w3c.github.io/vc-data-integrity/) Draft Community Group Report
|
||||
> - [Linked Data Cryptographic Suite Registry](https://w3c-ccg.github.io/ld-cryptosuite-registry/) Draft Community Group Report 29 December 2020
|
||||
> - [JSON Web Signature 2020](https://w3c-ccg.github.io/lds-jws2020/) Draft Community Group Report 09 February 2022
|
||||
> - [VC Proof Formats Test Suite, VC Data Model with JSON Web Signatures](https://identity.foundation/JWS-Test-Suite/) Unofficial Draft 09 March 2022 This implementation's compliance with the JWS Test Suite can be found here.
|
||||
> - Supports both JWT and Linked Data proof formats with [JOSE compliance](https://jose.readthedocs.io/en/latest/).
|
||||
@@ -0,0 +1,164 @@
|
||||
# Verifer Service
|
||||
|
||||
Refer to [Presentation Exchange](https://identity.foundation/presentation-exchange/)
|
||||
 for the process.
|
||||
|
||||
## Presentation Definition
|
||||
|
||||
1. The Verifier returns the file to the Holder.
|
||||
2. The Holder submits a packaged file that meets the format requirements to the Verifier, after filling in the contents according to the specifications.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Facebook Basic Info Presentation Definition",
|
||||
"purpose": "Provide your facebook basic info",
|
||||
"inputDescriptors": [
|
||||
{
|
||||
"id": "name",
|
||||
"name": "Name",
|
||||
"purpose": "Provide vc name",
|
||||
"format": {
|
||||
"jwt_vc": {
|
||||
"alg": ["EdDSA"]
|
||||
}
|
||||
},
|
||||
"constraints": {
|
||||
"fields": [
|
||||
{
|
||||
"path": ["$.credentialSubject.name"]
|
||||
}
|
||||
],
|
||||
"subject_is_issuer": "preferred"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "title",
|
||||
"name": "Title",
|
||||
"purpose": "Provide vc title",
|
||||
"format": {
|
||||
"jwt_vc": {
|
||||
"alg": ["EdDSA"]
|
||||
}
|
||||
},
|
||||
"constraints": {
|
||||
"fields": [
|
||||
{
|
||||
"path": ["$.credentialSubject.title"]
|
||||
}
|
||||
],
|
||||
"subject_is_issuer": "preferred"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "description",
|
||||
"name": "description",
|
||||
"purpose": "Provide vc description",
|
||||
"format": {
|
||||
"jwt_vc": {
|
||||
"alg": ["EdDSA"]
|
||||
}
|
||||
},
|
||||
"constraints": {
|
||||
"fields": [
|
||||
{
|
||||
"path": ["$.credentialSubject.description"]
|
||||
}
|
||||
],
|
||||
"subject_is_issuer": "preferred"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"id": "facebook_name",
|
||||
"name": "Provide your facebook name",
|
||||
"purpose": "Provide your facebook name",
|
||||
"format": {
|
||||
"jwt_vc": {
|
||||
"alg": ["EdDSA"]
|
||||
}
|
||||
},
|
||||
"constraints": {
|
||||
"fields": [
|
||||
{
|
||||
"path": ["$.credentialSubject.facebook_name"]
|
||||
}
|
||||
],
|
||||
"subject_is_issuer": "preferred"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "profile_image",
|
||||
"name": "Provide your facebook profile image",
|
||||
"purpose": "Provide your facebook profile image",
|
||||
"format": {
|
||||
"jwt_vc": {
|
||||
"alg": ["EdDSA"]
|
||||
}
|
||||
},
|
||||
"constraints": {
|
||||
"fields": [
|
||||
{
|
||||
"path": ["$.credentialSubject.profile_image"]
|
||||
}
|
||||
],
|
||||
"subject_is_issuer": "preferred"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "email",
|
||||
"name": "Provide your facebook email email info",
|
||||
"purpose": "Provide your facebook email info",
|
||||
"format": {
|
||||
"jwt_vc": {
|
||||
"alg": ["EdDSA"]
|
||||
}
|
||||
},
|
||||
"constraints": {
|
||||
"fields": [
|
||||
{
|
||||
"path": ["$.credentialSubject.email"]
|
||||
}
|
||||
],
|
||||
"subject_is_issuer": "preferred"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "facebook_id",
|
||||
"name": "Provide your facebook id",
|
||||
"purpose": "Provide your facebook id",
|
||||
"format": {
|
||||
"jwt_vc": {
|
||||
"alg": ["EdDSA"]
|
||||
}
|
||||
},
|
||||
"constraints": {
|
||||
"fields": [
|
||||
{
|
||||
"path": ["$.credentialSubject.facebook_id"]
|
||||
}
|
||||
],
|
||||
"subject_is_issuer": "preferred"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "picture_is_silhouette",
|
||||
"name": "Provide your facebook Picture is Silhouette",
|
||||
"purpose": "Provide your facebook picture_is_silhouette",
|
||||
"format": {
|
||||
"jwt_vc": {
|
||||
"alg": ["EdDSA"]
|
||||
}
|
||||
},
|
||||
"constraints": {
|
||||
"fields": [
|
||||
{
|
||||
"path": ["$.credentialSubject.picture_is_silhouette"]
|
||||
}
|
||||
],
|
||||
"subject_is_issuer": "preferred"
|
||||
}
|
||||
}
|
||||
],
|
||||
"author": ""
|
||||
}
|
||||
```
|
||||
92
docs/developer/contribute/olares.md
Normal file
92
docs/developer/contribute/olares.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Olares
|
||||
|
||||
## Directory structure
|
||||
|
||||
```
|
||||
olares
|
||||
|-- apps # olares built-in apps
|
||||
| |-- agent
|
||||
| |-- analytic
|
||||
| |-- market
|
||||
| |-- market-server
|
||||
| |-- argo
|
||||
| |-- desktop
|
||||
| |-- devbox
|
||||
| |-- vault
|
||||
| |-- files
|
||||
| |-- knowledge
|
||||
| |-- nitro
|
||||
| |-- notifications
|
||||
| |-- profile
|
||||
| |-- rss
|
||||
| |-- search
|
||||
| |-- settings
|
||||
| |-- system-apps
|
||||
| |-- wise
|
||||
| |-- wizard
|
||||
|-- build # olares installer
|
||||
| |-- installer
|
||||
| |-- manifest
|
||||
|-- frameworks # system runtime frameworks
|
||||
| |-- app-service
|
||||
| |-- backup-server
|
||||
| |-- bfl
|
||||
| |-- GPU
|
||||
| |-- l4-bfl-proxy
|
||||
| |-- osnode-init
|
||||
| |-- system-server
|
||||
| |-- tapr
|
||||
|-- libs # toolkit libs
|
||||
| |-- fs-lib
|
||||
|-- scripts # scripts for build or package the olares installer
|
||||
|-- third-party # third party libs or apps integrated in olares
|
||||
| |-- authelia
|
||||
| |-- headscale
|
||||
| |-- infisical
|
||||
| |-- juicefs
|
||||
| |-- ks-console
|
||||
| |-- ks-installer
|
||||
| |-- kube-state-metrics
|
||||
| |-- notification-mananger
|
||||
| |-- predixy
|
||||
| |-- redis-cluster-operator
|
||||
| |-- seafile-server
|
||||
| |-- seahub
|
||||
| |-- tailscale
|
||||
```
|
||||
|
||||
## How to install
|
||||
|
||||
```
|
||||
curl -fsSL https://olares.sh | bash -
|
||||
```
|
||||
|
||||
## How to build
|
||||
|
||||
```
|
||||
git clone https://github.com/beclab/olares
|
||||
|
||||
cd olares
|
||||
|
||||
bash scripts/build.sh
|
||||
|
||||
```
|
||||
|
||||
Run the above scripts, you will get the debug version installer package `install-wizard-debug.tar.gz`
|
||||
|
||||
## How to install debug version
|
||||
|
||||
```
|
||||
mkdir -p /path/to/unpack && cd /path/to/unpack
|
||||
|
||||
tar zxvf /path/to/olares/install-wizard-debug.tar.gz
|
||||
|
||||
make install VERSION=0.0.0-DEBUG
|
||||
|
||||
```
|
||||
|
||||
## How to uninstall
|
||||
|
||||
```bash
|
||||
olares-cli uninstall --all
|
||||
```
|
||||
7
docs/developer/contribute/overview.md
Normal file
7
docs/developer/contribute/overview.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Contributing To Olares
|
||||
|
||||
## Olares
|
||||
|
||||
## TermiPass
|
||||
|
||||
## Develop System App
|
||||
295
docs/developer/contribute/system-app/deployment.md
Normal file
295
docs/developer/contribute/system-app/deployment.md
Normal file
@@ -0,0 +1,295 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
---
|
||||
|
||||
# deployment.yaml
|
||||
|
||||
The system application need to be installed under the `user-space` namespace. Therefore, certain modifications are required:
|
||||
|
||||
1. Modify the `deployment.yaml` file in the Olares Application Chart.
|
||||
2. Change the original namespace of `deployment` and `service` to `user-space-{\\{ .Values.bfl.username }}`
|
||||
|
||||
```Yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: desktop-dev
|
||||
namespace: user-space-{{ .Values.bfl.username }}
|
||||
```
|
||||
|
||||
3. Add `annotations` and `labels` according to the configuration in the `deployment.yaml` file of the app in **Olares**.
|
||||
|
||||
```Yaml
|
||||
metadata:
|
||||
name: desktop-dev
|
||||
namespace: user-space-{{ .Values.bfl.username }}
|
||||
labels:
|
||||
app: desktop-dev
|
||||
applications.app.bytetrade.io/name: desktop-dev
|
||||
applications.app.bytetrade.io/owner: {{ .Values.bfl.username }}
|
||||
applications.app.bytetrade.io/author: bytetrade.io
|
||||
annotations:
|
||||
applications.app.bytetrade.io/icon: https://docs-dev.olares.com/icon.png
|
||||
applications.app.bytetrade.io/title: Desktop-dev
|
||||
applications.app.bytetrade.io/version: '0.0.1'
|
||||
|
||||
# Configuration of entrances here should be consistent with the configuration in OlaresManifest.yaml.
|
||||
applications.app.bytetrade.io/entrances: '[{"name":"desktop-frontend-dev", "host":"desktop-svc-dev", "port":80,"title":"Desktop-dev"}]'
|
||||
```
|
||||
|
||||
4. Modify service
|
||||
|
||||
```Yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: desktop-svc-dev
|
||||
namespace: user-space-{{ .Values.bfl.username }}
|
||||
spec:
|
||||
selector:
|
||||
app: desktop-dev
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 8080 # Please note, the port of the Node.js dev container is 8080. please switch to this port.
|
||||
```
|
||||
|
||||
5. Modify the section of entrances in `OlaresManifest.yaml`
|
||||
|
||||
```Yaml
|
||||
entrances:
|
||||
- name: desktop-frontend-dev # Same with annotation in deployment
|
||||
host: desktop-svc-dev # Same with the name in service
|
||||
port: 80
|
||||
icon: https://file.bttcdn.com/appstore/default/defaulticon.webp
|
||||
title: Desktop-dev
|
||||
authLevel: private
|
||||
openMethod: default
|
||||
```
|
||||
|
||||
6. Add service to provide `app-service` installation check
|
||||
|
||||
```Yaml
|
||||
# provide `app-service` installation check
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: desktop-svc-dev # Same with the name in original service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
type: ExternalName
|
||||
externalName: desktop-svc-dev.user-space-{{ .Values.bfl.username }}.svc.cluster.local
|
||||
ports:
|
||||
- protocol: TCP
|
||||
name: desktop
|
||||
port: 80
|
||||
targetPort: 80
|
||||
```
|
||||
|
||||
7. If you need to add a `local cache` or require access to the `user directory` in `JuiceFS`, you can add
|
||||
|
||||
```Yaml
|
||||
volumes:
|
||||
- name: appdata
|
||||
hostPath:
|
||||
type: DirectoryOrCreate
|
||||
path: {{ .Values.userspace.appData }}/desktop-dev
|
||||
|
||||
- name: userdata
|
||||
hostPath:
|
||||
type: DirectoryOrCreate
|
||||
path: {{ .Values.userspace.userData }}/desktop-dev
|
||||
|
||||
- name: appcache
|
||||
hostPath:
|
||||
type: DirectoryOrCreate
|
||||
path: {{ .Values.userspace.appCache }}/desktop-dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
:::details Example of a complete `deployment.yaml` file
|
||||
|
||||
```YAML
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: desktop-dev
|
||||
namespace: user-space-{{ .Values.bfl.username }}
|
||||
labels:
|
||||
app: desktop-dev
|
||||
applications.app.bytetrade.io/name: desktop-dev
|
||||
applications.app.bytetrade.io/owner: {{ .Values.bfl.username }}
|
||||
applications.app.bytetrade.io/author: bytetrade.io
|
||||
annotations:
|
||||
applications.app.bytetrade.io/icon: https://docs-dev.olares.com/icon.png
|
||||
applications.app.bytetrade.io/title: Desktop-dev
|
||||
applications.app.bytetrade.io/version: '0.0.1'
|
||||
applications.app.bytetrade.io/entrances: '[{"name":"desktop-frontend-dev", "host":"desktop-svc-dev", "port":80,"title":"Desktop-dev"}]'
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: desktop-dev
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: desktop-dev
|
||||
spec:
|
||||
volumes:
|
||||
- name: olares-sidecar-config
|
||||
configMap:
|
||||
name: sidecar-configs
|
||||
items:
|
||||
- key: envoy.yaml
|
||||
path: envoy.yaml
|
||||
- name: appdata
|
||||
hostPath:
|
||||
type: DirectoryOrCreate
|
||||
path: {{ .Values.userspace.appData }}/desktop-dev
|
||||
|
||||
- name: userdata
|
||||
hostPath:
|
||||
type: DirectoryOrCreate
|
||||
path: {{ .Values.userspace.userData }}/desktop-dev
|
||||
|
||||
- name: appcache
|
||||
hostPath:
|
||||
type: DirectoryOrCreate
|
||||
path: {{ .Values.userspace.appCache }}/desktop-dev
|
||||
|
||||
initContainers:
|
||||
- name: olares-sidecar-init
|
||||
image: openservicemesh/init:v1.2.3
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
privileged: true
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
runAsNonRoot: false
|
||||
runAsUser: 0
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
iptables-restore --noflush <<EOF
|
||||
# sidecar interception rules
|
||||
*nat
|
||||
:PROXY_IN_REDIRECT - [0:0]
|
||||
:PROXY_INBOUND - [0:0]
|
||||
-A PROXY_IN_REDIRECT -p tcp -j REDIRECT --to-port 15003
|
||||
-A PROXY_INBOUND -p tcp --dport 15000 -j RETURN
|
||||
-A PROXY_INBOUND -p tcp -j PROXY_IN_REDIRECT
|
||||
-A PREROUTING -p tcp -j PROXY_INBOUND
|
||||
COMMIT
|
||||
EOF
|
||||
|
||||
env:
|
||||
- name: POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
apiVersion: v1
|
||||
fieldPath: status.podIP
|
||||
containers:
|
||||
- name: desktop
|
||||
image: "aboveos/node-ts-dev"
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: port
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
resources:
|
||||
requests:
|
||||
cpu: "50m"
|
||||
memory: 100Mi
|
||||
limits:
|
||||
cpu: "0.5"
|
||||
memory: 2Gi
|
||||
volumeMounts:
|
||||
- name: appdata
|
||||
mountPath: /opt/code
|
||||
- name: appcache
|
||||
mountPath: /root/.config
|
||||
- name: olares-envoy-sidecar
|
||||
image: envoyproxy/envoy-distroless:v1.25.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
runAsUser: 1000
|
||||
ports:
|
||||
- name: proxy-admin
|
||||
containerPort: 15000
|
||||
- name: proxy-inbound
|
||||
containerPort: 15003
|
||||
volumeMounts:
|
||||
- name: olares-sidecar-config
|
||||
readOnly: true
|
||||
mountPath: /etc/envoy/envoy.yaml
|
||||
subPath: envoy.yaml
|
||||
command:
|
||||
- /usr/local/bin/envoy
|
||||
- --log-level
|
||||
- debug
|
||||
- -c
|
||||
- /etc/envoy/envoy.yaml
|
||||
env:
|
||||
- name: POD_UID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.uid
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
resources:
|
||||
requests:
|
||||
cpu: "50m"
|
||||
memory: 100Mi
|
||||
limits:
|
||||
cpu: "0.5"
|
||||
memory: 500Mi
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: desktop-svc-dev
|
||||
namespace: user-space-{{ .Values.bfl.username }}
|
||||
spec:
|
||||
selector:
|
||||
app: desktop-dev
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 8080
|
||||
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: desktop-svc-dev
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
type: ExternalName
|
||||
externalName: desktop-svc-dev.user-space-{{ .Values.bfl.username }}.svc.cluster.local
|
||||
ports:
|
||||
- protocol: TCP
|
||||
name: desktop
|
||||
port: 80
|
||||
targetPort: 80
|
||||
```
|
||||
|
||||
:::
|
||||
239
docs/developer/contribute/system-app/install.md
Normal file
239
docs/developer/contribute/system-app/install.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# Install System Application
|
||||
|
||||
## Install
|
||||
|
||||
Install the system app from **DevBox**. Due to the installation mechanism, the desktop may not receive proper notifications. Consequently, you might need to manually refresh the page to see the icon.
|
||||
|
||||

|
||||
|
||||
Once the application is successfully installed and in the **'Running'** state, you can access the default welcome page of VSCode. Simply click the **'Open IDE'** button on the **'Containers'** page.
|
||||
|
||||

|
||||
|
||||
::: tip
|
||||
There is no need to bind in this case.
|
||||
:::
|
||||
|
||||
## Clone Code
|
||||
|
||||
- Once you've entered the IDE, begin by installing the gh tool.
|
||||
|
||||
```shell
|
||||
apt install gh
|
||||
```
|
||||
|
||||
- Log in to GitHub
|
||||
|
||||
```shell
|
||||
gh auth login
|
||||
```
|
||||
|
||||
- Clone the code after logging in.
|
||||
|
||||
```shell
|
||||
cd /opt/code && gh repo clone beclab/desktop
|
||||
```
|
||||
:::tip
|
||||
It's recommended to clone the code into the `/opt/code` directory. This is because the `code server` is run by the `root`, while `nginx` is run by a `non-root user`. In this example, the `/opt/code` directory has been mounted to the `application data` directory of the node. So that, the code will remain saved even if the pod is restarted.
|
||||
:::
|
||||
|
||||
## Run the Application
|
||||
|
||||
Run your application in the terminal of the IDE. For example:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Nginx
|
||||
|
||||
- Open the folder `/opt/code/desktop-v1` in **VS Code**
|
||||
|
||||
- To run debugging, first modify the `nginx` configuration in the container.
|
||||
|
||||
- Use **VS Code** to open the configuration file located at `/etc/nginx/conf.d/default.conf`.
|
||||
|
||||
- Modify the proxies for front-end and back-end testing.
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:9000;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $http_connection;
|
||||
proxy_set_header Accept-Encoding gzip;
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_pass http://127.0.0.1:3010;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $http_connection;
|
||||
proxy_set_header Accept-Encoding gzip;
|
||||
}
|
||||
```
|
||||
|
||||
You can also configure the proxy for other services in project, as an example.
|
||||
|
||||
```nginx
|
||||
location /api/logout {
|
||||
add_header 'Access-Control-Allow-Headers' 'x-api-nonce,x-api-ts,x-api-ver,x-api-source';
|
||||
proxy_pass http://authelia-svc;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
}
|
||||
```
|
||||
The configuration is exactly the same as when running in Olares, and no further modifications are required.
|
||||
|
||||
- Reload Nginx after modification.
|
||||
|
||||
```shell
|
||||
nginx -s reload
|
||||
```
|
||||
|
||||
- If you want to keep the `default.conf` of `nginx`, you can save it in either `/root/.config` or the code repository. Then, each time you restart the pod, go to VS Code Terminal and create a new soft link.
|
||||
|
||||
```shell
|
||||
cd /etc/nginx/conf.d
|
||||
rm default.conf
|
||||
ln -s /root/.config/default.conf
|
||||
|
||||
nginx -s reload
|
||||
```
|
||||
:::details Example of a complete `nginx.conf` file (Desktop as an example)
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 8080 default_server;
|
||||
root /app;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:9000;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $http_connection;
|
||||
proxy_set_header Accept-Encoding gzip;
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_pass http://127.0.0.1:3010;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $http_connection;
|
||||
proxy_set_header Accept-Encoding gzip;
|
||||
}
|
||||
|
||||
location /server {
|
||||
proxy_pass http://127.0.0.1:3010;
|
||||
# rewrite ^/server(.*)$ $1 break;
|
||||
|
||||
# Add original-request-related headers
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
}
|
||||
|
||||
location /notification {
|
||||
proxy_pass http://127.0.0.1:3010;
|
||||
# rewrite ^/server(.*)$ $1 break;
|
||||
|
||||
# Add original-request-related headers
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
}
|
||||
|
||||
location /video {
|
||||
proxy_pass http://127.0.0.1:3010;
|
||||
# rewrite ^/server(.*)$ $1 break;
|
||||
|
||||
# Add original-request-related headers
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
}
|
||||
|
||||
location /api/logout {
|
||||
add_header 'Access-Control-Allow-Headers' 'x-api-nonce,x-api-ts,x-api-ver,x-api-source';
|
||||
proxy_pass http://authelia-svc;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
}
|
||||
|
||||
location /api/device {
|
||||
add_header Access-Control-Allow-Headers "access-control-allow-headers,access-control-allow-methods,access-control-allow-origin,content-type,x-auth,x-unauth-error,x-authorization";
|
||||
add_header Access-Control-Allow-Methods "PUT, GET, DELETE, POST, OPTIONS";
|
||||
add_header Access-Control-Allow-Origin $http_origin;
|
||||
add_header Access-Control-Allow-Credentials true;
|
||||
|
||||
proxy_pass http://settings-service;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
}
|
||||
|
||||
location /api/refresh {
|
||||
add_header Access-Control-Allow-Headers "access-control-allow-headers,access-control-allow-methods,access-control-allow-origin,content-type,x-auth,x-unauth-error,x-authorization";
|
||||
add_header Access-Control-Allow-Methods "PUT, GET, DELETE, POST, OPTIONS";
|
||||
add_header Access-Control-Allow-Origin $http_origin;
|
||||
add_header Access-Control-Allow-Credentials true;
|
||||
|
||||
proxy_pass http://authelia-backend-svc:9091;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
}
|
||||
|
||||
location /proxy/3000/ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-real-ip $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $http_connection;
|
||||
proxy_set_header Accept-Encoding gzip;
|
||||
add_header Access-Control-Allow-Headers "Accept, Content-Type, Accept-Encoding";
|
||||
add_header Access-Control-Allow-Methods "GET, OPTIONS";
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
After the nginx proxy is activated, you can initiate the front-end and back-end services in the VS Code Terminal.
|
||||
|
||||
::: warning
|
||||
The service cannot be started on ports 80, 8080, and 3000.
|
||||
:::
|
||||
|
||||
## Preview
|
||||
|
||||
After install the application, you can preview it by clicking on the app icon on the Olares.
|
||||
|
||||

|
||||
100
docs/developer/contribute/system-app/olares-manifest.md
Normal file
100
docs/developer/contribute/system-app/olares-manifest.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# OlaresManifest.yaml
|
||||
|
||||
## Permission
|
||||
|
||||
If you need to access the interface of `provider`, you can add following content in the `permissions` section of the `OlaresManifest.yaml` file.
|
||||
```Yaml
|
||||
permission:
|
||||
sysData:
|
||||
- dataType: app
|
||||
group: service.bfl
|
||||
version: v1
|
||||
ops:
|
||||
- InstallDevApp
|
||||
```
|
||||
|
||||
## Reference variable in env
|
||||
|
||||
You can reference the variable in the `env` section of the `deployment.yaml` file.
|
||||
|
||||
```Yaml
|
||||
env:
|
||||
- name: OS_APP_KEY
|
||||
value: {{ .Values.os.appKey }} # Please note, you need to replace it with .Values.os.desktop.appKey when submit to the install wizard.
|
||||
- name: OS_APP_SECRET
|
||||
value: {{ .Values.os.appSecret }} # Please note, you need to replace it with .Values.os.desktop.appSecret when submit to the install wizard.
|
||||
- name: OS_SYSTEM_SERVER
|
||||
value: system-server.user-system-{{ .Values.bfl.username }}
|
||||
```
|
||||
|
||||
---
|
||||
:::details Example of a complete `OlaresManifest.yaml` file
|
||||
```Yaml
|
||||
olaresManifest.version: 1
|
||||
olaresManifest.type: app
|
||||
metadata:
|
||||
name: desktop
|
||||
icon: https://file.bttcdn.com/appstore/default/defaulticon.webp
|
||||
description: app desktop
|
||||
appid: desktop
|
||||
title: desktop
|
||||
version: 0.0.2
|
||||
categories:
|
||||
- dev
|
||||
entrances:
|
||||
- name: desktop-frontend-dev
|
||||
host: desktop-svc-dev
|
||||
port: 80
|
||||
icon: https://file.bttcdn.com/appstore/default/defaulticon.webp
|
||||
title: Desktop-dev
|
||||
authLevel: private
|
||||
openMethod: default
|
||||
spec:
|
||||
versionName: 0.0.1
|
||||
requiredMemory: 2Gi
|
||||
requiredDisk: 50Mi
|
||||
supportArch:
|
||||
- amd64
|
||||
requiredCpu: 50m
|
||||
limitedMemory: 3Gi
|
||||
limitedCpu: 1000m
|
||||
permission:
|
||||
appData: true
|
||||
appCache: true
|
||||
userData: []
|
||||
sysData:
|
||||
- group: service.bfl
|
||||
dataType: app
|
||||
version: v1
|
||||
ops:
|
||||
- UserApps
|
||||
- group: service.appstore
|
||||
dataType: app
|
||||
version: v1
|
||||
ops:
|
||||
- UninstallDevApp
|
||||
- group: service.bfl
|
||||
dataType: datastore
|
||||
version: v1
|
||||
ops:
|
||||
- GetKey
|
||||
- GetKeyPrefix
|
||||
- SetKey
|
||||
- DeleteKey
|
||||
- group: service.files
|
||||
dataType: files
|
||||
version: v1
|
||||
ops:
|
||||
- Query
|
||||
options:
|
||||
resetCookie:
|
||||
enabled: false
|
||||
dependencies:
|
||||
- name: olares
|
||||
version: '>=0.1.0'
|
||||
type: system
|
||||
appScope:
|
||||
clusterScoped: false
|
||||
appRef: []
|
||||
```
|
||||
:::
|
||||
127
docs/developer/contribute/system-app/other.md
Normal file
127
docs/developer/contribute/system-app/other.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Other
|
||||
|
||||
## Change Provider Registry
|
||||
|
||||
If you need to connect the `provider` of the original system application to your development environment, you can manually change the address of the `ProviderRegistry`. In the **'CRDs'** page of the control hub, locate `sys.bytetrade.io` and then find the `ProviderRegistry`.
|
||||
|
||||

|
||||
|
||||
In the list, navigate to the app you wish to replace (in this case, **desktop-notification**). Click on the **'...'** button on the right and select **'Edit YAML'**. Modify the `endpoint` in the **YAML** file and direct it towards the service address of your **developing app**. Click **'OK'** to save and apply the changes.
|
||||
|
||||

|
||||
|
||||
|
||||
## Vite configuration
|
||||
|
||||
If your frontend project uses **Vite**, you need to add an **HMR** configuration. In development mode, **Vite** initiates a **WebSocket** to receive code reload notifications from the server. The default **WebSocket** port matches the server's startup port. However, if the development app uses an **Nginx proxy** it will operate on the default port 443. Therefore, some modifications are required.
|
||||
|
||||
If you are using **Quasar** + **Vite**, add the following in the `quasar.config.js`:
|
||||
|
||||
```js
|
||||
extendViteConf(viteConf) {
|
||||
viteConf.server.hmr = {clientPort: 443};
|
||||
},
|
||||
```
|
||||
If it is a standalone **Vite** project, modify `vite.config.js` as:
|
||||
```js
|
||||
export default defineConfig({
|
||||
server: {
|
||||
hmr: {
|
||||
clientPort: 443,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Use System Database
|
||||
|
||||
You can add **system databases** by adding `MiddlewareRequest` in the `deployment.yaml`.
|
||||
Using the **Postgres** in Dify as an example:
|
||||
```Yaml
|
||||
apiVersion: apr.bytetrade.io/v1alpha1
|
||||
kind: MiddlewareRequest
|
||||
metadata:
|
||||
name: dify-pg
|
||||
namespace: os-system
|
||||
spec:
|
||||
app: dify
|
||||
appNamespace: os-system
|
||||
middleware: postgres
|
||||
postgreSQL:
|
||||
user: dify_os_system
|
||||
password:
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
key: pg_password
|
||||
name: dify-secrets
|
||||
databases:
|
||||
- name: dify
|
||||
```
|
||||
|
||||
You need to set a `secret` of password for adding a `MiddlewareRequest`,
|
||||
|
||||
```Yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: dify-secrets
|
||||
namespace: {{ .Release.Namespace }}
|
||||
type: Opaque
|
||||
data:
|
||||
pg_password: {{ $pg_password }} # Password can be randomly generated, then Base64 encoded
|
||||
```
|
||||
|
||||
Configure the following in the pod where you want to use the database.
|
||||
|
||||
```Yaml
|
||||
env:
|
||||
- name: DB_USERNAME
|
||||
value: dify_user_space_{{ .Values.bfl.name }} # Please note, you need to add a namespace suffix when use the username configured in the MiddlewareRequest above. For username in Postgres, you also need to replace - with _
|
||||
- name: DB_PASSWORD
|
||||
value: {{ $pg_password | b64dec }} # The decoded password configured above
|
||||
- name: DB_HOST
|
||||
value: citus-master-svc.user-system-{{ .Values.bfl.username }} # HOST address,
|
||||
# For Redis: redis-cluster-proxy.user-system-{{ .Values.bfl.username }}
|
||||
# For Mongo: mongo-cluster-mongos.user-system-{{ .Values.bfl.username }}
|
||||
- name: DB_PORT
|
||||
value: '5432'
|
||||
# For Redis: 6379
|
||||
# For Mongo: 27017
|
||||
- name: DB_DATABASE
|
||||
value: os_system_dify # Please note, you need to add a namespace suffix when use the database name configured in the MiddlewareRequest above. For username in Postgres, you also need to replace - with _
|
||||
```
|
||||
|
||||
You can also concatenate the `dsn` link:
|
||||
|
||||
```Yaml
|
||||
postgres://dify_{{ .Values.bfl.username }}:{{ $pg_password_data }}@citus-master-svc.user-system-{{ .Values.bfl.username }}/user_space_{{ .Values.bfl.username }}_dify?sslmode=disable
|
||||
|
||||
mongodb://dify-{{ .Values.bfl.username }}:{{ $mongo_password_data }}@mongo-cluster-mongos.user-system-{{ .Values.bfl.username }}:27017/{{ .Release.Namespace }}_dify
|
||||
|
||||
redis://:{{ $redis_password | b64dec }}@redis-cluster-proxy.user-system-{{ .Values.bfl.username }}:6379/0 # Please note, Since the system uses Redis Cluster, the database name must set to '0'.
|
||||
```
|
||||
|
||||
To register a provider, you need to add a `ProviderRegistry` in the `deployment.yaml` file.
|
||||
|
||||
```Yaml
|
||||
apiVersion: sys.bytetrade.io/v1alpha1
|
||||
kind: ProviderRegistry
|
||||
metadata:
|
||||
name: desktop-provider-dev # The name cannot be duplicated with the existing one.
|
||||
namespace: user-system-{{ .Values.bfl.username }}
|
||||
spec:
|
||||
|
||||
dataType: config-dev # The dataTypes cannot be duplicated with an existing one. If you want to replace an existing dataType, you need to delete it first.
|
||||
deployment: desktop-dev
|
||||
description: Set Desktop Config
|
||||
endpoint: desktop-svc-dev.{{ .Release.Namespace }} # The address of the provider, pointing to your developing app
|
||||
group: service.desktop
|
||||
kind: provider
|
||||
namespace: {{ .Release.Namespace }}
|
||||
opApis:
|
||||
- name: Update
|
||||
uri: /server/updateDesktopConfig
|
||||
version: v1
|
||||
status:
|
||||
state: active
|
||||
```
|
||||
19
docs/developer/contribute/system-app/overview.md
Normal file
19
docs/developer/contribute/system-app/overview.md
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
# Develop System Application for Olares
|
||||
|
||||
::: tip Prerequisites Knowledge
|
||||
- Debugging third-party applications with **DevBox**
|
||||
- Developing applications with **DevBox**
|
||||
- Understanding the architecture and mechanisms of **Olares**
|
||||
:::
|
||||
|
||||
After a year and a half of work, the Olares team now uses **DevBox** for developing system applications.
|
||||
|
||||
We will show you how to debug issues in the frontend code of **Desktop** using **DevBox**.
|
||||
1. Create an application in DevBox
|
||||
2. Configure [deployment.yaml](./deployment)
|
||||
3. Configure [OlaresManifest.yaml](./olares-manifest)
|
||||
4. Install the application
|
||||
5. Configure [nginx.conf](./install)
|
||||
|
||||
There are also some [other](./other) aspects to consider in development.
|
||||
80
docs/developer/develop/advanced/account.md
Normal file
80
docs/developer/develop/advanced/account.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Account
|
||||
|
||||
If an app in Olares wants to use the system user as the app's user, it can obtain the user information by defining a `SysEventRegistry` in application chart to receive system user event callbacks.
|
||||
|
||||
- Define `user create` callback
|
||||
|
||||
```yaml
|
||||
apiVersion: apr.bytetrade.io/v1alpha1
|
||||
kind: SysEventRegistry
|
||||
metadata:
|
||||
name: user-create-cb
|
||||
namespace: "{{ .Release.Namespace }}"
|
||||
spec:
|
||||
type: subscriber
|
||||
event: user.create
|
||||
callback: http://app-svc.{{ .Release.Namespace }}:8080/callback/create
|
||||
```
|
||||
Response data
|
||||
```http
|
||||
POST /callback/create HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "user1",
|
||||
"role": "workspace-manager",
|
||||
"email": "user1@xxx.com"
|
||||
}
|
||||
```
|
||||
|
||||
- Define `user delete` callback
|
||||
|
||||
```yaml
|
||||
apiVersion: apr.bytetrade.io/v1alpha1
|
||||
kind: SysEventRegistry
|
||||
metadata:
|
||||
name: user-delete-cb
|
||||
namespace: "{{ .Release.Namespace }}"
|
||||
spec:
|
||||
type: subscriber
|
||||
event: user.delete
|
||||
callback: http://app-svc.{{ .Release.Namespace }}:8080/callback/delete
|
||||
```
|
||||
Response data
|
||||
```http
|
||||
POST /callback/delete HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "user1",
|
||||
"email": "user1@xxx.com"
|
||||
}
|
||||
```
|
||||
|
||||
- Define `user active` callback
|
||||
|
||||
```yaml
|
||||
apiVersion: apr.bytetrade.io/v1alpha1
|
||||
kind: SysEventRegistry
|
||||
metadata:
|
||||
name: user-active-cb
|
||||
namespace: "{{ .Release.Namespace }}"
|
||||
spec:
|
||||
type: subscriber
|
||||
event: user.active
|
||||
callback: http://app-svc.{{ .Release.Namespace }}:8080/callback/activate
|
||||
```
|
||||
Response data
|
||||
```http
|
||||
POST /callback/activate HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "user1",
|
||||
"email": "user1@xxx.com"
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
To receive system callback notifications, the app must define a service and register it. For instance, the `app-svc` mentioned above.
|
||||
:::
|
||||
547
docs/developer/develop/advanced/ai.md
Normal file
547
docs/developer/develop/advanced/ai.md
Normal file
@@ -0,0 +1,547 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
---
|
||||
|
||||
# AI
|
||||
|
||||
## API Prefix
|
||||
|
||||
`agent.{username}.olares.com/api/controllers/console/api`
|
||||
|
||||
## Basic Application Management API
|
||||
### Get App List
|
||||
- **Request**
|
||||
- **URL**: `/apps`
|
||||
- **Method**: `GET`
|
||||
- **URL Parameters**: `/apps?page=1&limit=30&name=Ashia`
|
||||
:::tip
|
||||
Most of the APIs listed in this document require the `app_id`, which can be obtained from the response of this API.
|
||||
:::
|
||||
|
||||
### Create App
|
||||
- **Request**
|
||||
- **URL**: `/apps`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**:
|
||||
```json
|
||||
{
|
||||
"name": "TEST",
|
||||
"icon": "🤖",
|
||||
"icon_background": "#FFEAD5",
|
||||
"mode": "agent-chat",
|
||||
"description": "JUST A TEST"
|
||||
}
|
||||
```
|
||||
|
||||
### Get App Details
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}`
|
||||
- **Method**: `GET`
|
||||
- **Body Example**: `null`
|
||||
|
||||
### Delete App
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}`
|
||||
- **Method**: `DELETE`
|
||||
- **Body Example**: `null`
|
||||
|
||||
### Copy App
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/copy`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**:
|
||||
```json
|
||||
{
|
||||
"name": "Ashia-2",
|
||||
"icon": "🤖",
|
||||
"icon_background": "#FFEAD5",
|
||||
"mode": "agent-chat"
|
||||
}
|
||||
```
|
||||
|
||||
### Rename App
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/name`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**:
|
||||
```json
|
||||
{
|
||||
"name": "Ashia—34"
|
||||
}
|
||||
```
|
||||
|
||||
### Change App Icon
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/icon`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**:
|
||||
```json
|
||||
{
|
||||
"icon": "heavy_check_mark"
|
||||
}
|
||||
```
|
||||
|
||||
### App Web Access Control
|
||||
> Whether the app can be accessed from the site.
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/site-enable`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**:
|
||||
```json
|
||||
{
|
||||
"enable_site": true
|
||||
}
|
||||
```
|
||||
|
||||
### App API Access Control
|
||||
> Whether the app can be accessed via API.
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/api-enable`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**:
|
||||
```json
|
||||
{
|
||||
"enable_api": true
|
||||
}
|
||||
```
|
||||
|
||||
## Application Function API
|
||||
### Text Generation
|
||||
> Execution interface for text generation APP
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/completion-messages`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**:
|
||||
:::details
|
||||
```json
|
||||
{
|
||||
"inputs": {
|
||||
"query": "Hello~"
|
||||
},
|
||||
"model_config": {
|
||||
"pre_prompt": "{{query}}",
|
||||
"prompt_type": "simple",
|
||||
"chat_prompt_config": {},
|
||||
"completion_prompt_config": {},
|
||||
"user_input_form": [
|
||||
{
|
||||
"paragraph": {
|
||||
"label": "Query",
|
||||
"variable": "query",
|
||||
"required": true,
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"dataset_query_variable": "",
|
||||
"opening_statement": null,
|
||||
"suggested_questions_after_answer": {
|
||||
"enabled": false
|
||||
},
|
||||
"speech_to_text": {
|
||||
"enabled": false
|
||||
},
|
||||
"retriever_resource": {
|
||||
"enabled": false
|
||||
},
|
||||
"sensitive_word_avoidance": {
|
||||
"enabled": false,
|
||||
"type": "",
|
||||
"configs": []
|
||||
},
|
||||
"more_like_this": {
|
||||
"enabled": false
|
||||
},
|
||||
"model": {
|
||||
"provider": "openai_api_compatible",
|
||||
"name": "nitro",
|
||||
"mode": "chat",
|
||||
"completion_params": {
|
||||
"temperature": 0.7,
|
||||
"top_p": 1,
|
||||
"frequency_penalty": 0,
|
||||
"presence_penalty": 0,
|
||||
"max_tokens": 512
|
||||
}
|
||||
},
|
||||
"text_to_speech": {
|
||||
"enabled": false,
|
||||
"voice": "",
|
||||
"language": ""
|
||||
},
|
||||
"agent_mode": {
|
||||
"enabled": false,
|
||||
"tools": []
|
||||
},
|
||||
"dataset_configs": {
|
||||
"retrieval_model": "single",
|
||||
"datasets": {
|
||||
"datasets": []
|
||||
}
|
||||
},
|
||||
"file_upload": {
|
||||
"image": {
|
||||
"enabled": false,
|
||||
"number_limits": 3,
|
||||
"detail": "high",
|
||||
"transfer_methods": [
|
||||
"remote_url",
|
||||
"local_file"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"response_mode": "streaming"
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
### Stop Text Generation
|
||||
> Interruption interface for text generation APP
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/completion-messages/{string:task_id}/stop`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**: `null`
|
||||
:::tip
|
||||
The `task_id` required in this API can be obtained from the response (streaming) of the [Text Generation](#text-generation) API.
|
||||
:::
|
||||
|
||||
### Chat
|
||||
> Execution interface for chat APP
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/chat-messages`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**:
|
||||
:::details
|
||||
```json
|
||||
{
|
||||
"response_mode": "streaming",
|
||||
"conversation_id": "",
|
||||
"query": "Hello~",
|
||||
"inputs": {},
|
||||
"model_config": {
|
||||
"pre_prompt": "",
|
||||
"prompt_type": "simple",
|
||||
"chat_prompt_config": {},
|
||||
"completion_prompt_config": {},
|
||||
"user_input_form": [],
|
||||
"dataset_query_variable": "",
|
||||
"opening_statement": "",
|
||||
"more_like_this": {
|
||||
"enabled": false
|
||||
},
|
||||
"suggested_questions": [],
|
||||
"suggested_questions_after_answer": {
|
||||
"enabled": false
|
||||
},
|
||||
"text_to_speech": {
|
||||
"enabled": false,
|
||||
"voice": "",
|
||||
"language": ""
|
||||
},
|
||||
"speech_to_text": {
|
||||
"enabled": false
|
||||
},
|
||||
"retriever_resource": {
|
||||
"enabled": false
|
||||
},
|
||||
"sensitive_word_avoidance": {
|
||||
"enabled": false
|
||||
},
|
||||
"agent_mode": {
|
||||
"max_iteration": 5,
|
||||
"enabled": true,
|
||||
"tools": [],
|
||||
"strategy": "react"
|
||||
},
|
||||
"dataset_configs": {
|
||||
"retrieval_model": "single",
|
||||
"datasets": {
|
||||
"datasets": []
|
||||
}
|
||||
},
|
||||
"file_upload": {
|
||||
"image": {
|
||||
"enabled": false,
|
||||
"number_limits": 2,
|
||||
"detail": "low",
|
||||
"transfer_methods": [
|
||||
"local_file"
|
||||
]
|
||||
}
|
||||
},
|
||||
"annotation_reply": {
|
||||
"enabled": false
|
||||
},
|
||||
"supportAnnotation": true,
|
||||
"appId": "2c937aae-f4f2-4cf9-b6e2-f2f2756858c0",
|
||||
"supportCitationHitInfo": true,
|
||||
"model": {
|
||||
"provider": "openai_api_compatible",
|
||||
"name": "nitro",
|
||||
"mode": "chat",
|
||||
"completion_params": {
|
||||
"temperature": 2,
|
||||
"top_p": 1,
|
||||
"frequency_penalty": 0,
|
||||
"presence_penalty": 0,
|
||||
"max_tokens": 512,
|
||||
"stop": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
|
||||
### Stop Chat
|
||||
> Interruption interface for chat APP
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/chat-messages/{string:task_id}/stop`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**: `null`
|
||||
:::tip
|
||||
The `task_id` required in this API can be obtained from the response (streaming) of the [Chat](#chat) API.
|
||||
:::
|
||||
|
||||
### Get Conversations List (Text Generation)
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/completion-conversations`
|
||||
- **Method**: `GET`
|
||||
- **URL Parameters**: `/apps/{uuid:app_id}/completion-conversations?page=1&limit=30`
|
||||
:::tip
|
||||
the Conversations (Text Generation) APIs listed below require the `conversation_id`, which can be obtained from the response of this API.
|
||||
:::
|
||||
|
||||
### Get Conversations Details (Text Generation)
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/completion-conversations/{uuid:conversation_id}`
|
||||
- **Method**: `GET`
|
||||
- **Body Example**: `null`
|
||||
:::tip
|
||||
The Conversations (Text Generation) APIs listed below require the `message_id`, which can be obtained from the response of this API.
|
||||
:::
|
||||
|
||||
### Delete Conversations Details (Text Generation)
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/completion-conversations/{uuid:conversation_id}`
|
||||
- **Method**: `DELETE`
|
||||
- **Body Example**: `null`
|
||||
|
||||
### Get Conversations List (Chat)
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/chat-conversations`
|
||||
- **Method**: `GET`
|
||||
- **URL Parameters**: `/apps/{uuid:app_id}/chat-conversations?page=1&limit=30`
|
||||
:::tip
|
||||
the Conversations (Chat) APIs listed below require the `conversation_id`, which can be obtained from the response of this API.
|
||||
:::
|
||||
|
||||
### Get Conversations Details (Chat)
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/chat-conversations/{uuid:conversation_id}`
|
||||
- **Method**: `GET`
|
||||
- **Body Example**: `null`
|
||||
:::tip
|
||||
The Conversations (Chat) APIs listed below require the `message_id`, which can be obtained from the response of this API.
|
||||
:::
|
||||
|
||||
### Delete Conversations Details (Chat)
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/chat-conversations/{uuid:conversation_id}`
|
||||
- **Method**: `DELETE`
|
||||
- **Body Example**: `null`
|
||||
|
||||
### Get Suggested Questions (Chat)
|
||||
> In a chat APP, get the suggested questions that can be asked after the AI gives a response
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/chat-messages/{uuid:message_id}/suggested-questions`
|
||||
- **Method**: `GET`
|
||||
- **Body Example**: `null`
|
||||
|
||||
### Get Message List (Chat)
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/chat-messages`
|
||||
- **Method**: `GET`
|
||||
- **URL Parameters**: `/apps/{uuid:app_id}/chat-messages?conversation_id={conversation_id}`
|
||||
|
||||
### Message Feedback
|
||||
> Give like or dislike feedback to the message from the APP
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/feedbacks`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**:
|
||||
```json
|
||||
{
|
||||
"rating": "like" // "like" | "dislike" | null
|
||||
}
|
||||
```
|
||||
|
||||
### Message Annotation
|
||||
> Give annotation to the message from the APP (Text Generation)
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/annotations`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**:
|
||||
```json
|
||||
{
|
||||
"message_id": "2b79fdad-e513-45ef-9532-8de5086cb81c",
|
||||
"question": "query:How are you?",
|
||||
"answer": "some answer messages"
|
||||
}
|
||||
```
|
||||
|
||||
### Count Annotation
|
||||
> Get the current number of annotations of the APP's message
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/annotations/count`
|
||||
- **Method**: `GET`
|
||||
- **Body Example**: `null`
|
||||
|
||||
|
||||
### Get Message Details (Chat)
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/messages/{uuid:message_id}`
|
||||
- **Method**: `GET`
|
||||
- **Body Example**: `null`
|
||||
|
||||
## Advanced Application Management API
|
||||
|
||||
### Model Config
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/model-config`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**:
|
||||
:::details
|
||||
```json
|
||||
{
|
||||
"pre_prompt": "",
|
||||
"prompt_type": "simple",
|
||||
"chat_prompt_config": {},
|
||||
"completion_prompt_config": {},
|
||||
"user_input_form": [],
|
||||
"dataset_query_variable": "",
|
||||
"opening_statement": "",
|
||||
"suggested_questions": [],
|
||||
"more_like_this": {
|
||||
"enabled": false
|
||||
},
|
||||
"suggested_questions_after_answer": {
|
||||
"enabled": false
|
||||
},
|
||||
"speech_to_text": {
|
||||
"enabled": false
|
||||
},
|
||||
"text_to_speech": {
|
||||
"enabled": false,
|
||||
"language": "",
|
||||
"voice": ""
|
||||
},
|
||||
"retriever_resource": {
|
||||
"enabled": false
|
||||
},
|
||||
"sensitive_word_avoidance": {
|
||||
"enabled": false
|
||||
},
|
||||
"agent_mode": {
|
||||
"max_iteration": 5,
|
||||
"enabled": true,
|
||||
"strategy": "react",
|
||||
"tools": []
|
||||
},
|
||||
"model": {
|
||||
"provider": "openai_api_compatible",
|
||||
"name": "nitro",
|
||||
"mode": "chat",
|
||||
"completion_params": {
|
||||
"frequency_penalty": 0,
|
||||
"max_tokens": 512,
|
||||
"presence_penalty": 0,
|
||||
"stop": [],
|
||||
"temperature": 2,
|
||||
"top_p": 1
|
||||
}
|
||||
},
|
||||
"dataset_configs": {
|
||||
"retrieval_model": "single",
|
||||
"datasets": {
|
||||
"datasets": []
|
||||
}
|
||||
},
|
||||
"file_upload": {
|
||||
"image": {
|
||||
"enabled": false,
|
||||
"number_limits": 2,
|
||||
"detail": "low",
|
||||
"transfer_methods": [
|
||||
"local_file"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
### Change APP Basic Info
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/site`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**:
|
||||
```json
|
||||
{
|
||||
"title": "Ashias-23",
|
||||
"icon": "grin",
|
||||
"icon_background": "#000000",
|
||||
"description": "How do you do~"
|
||||
}
|
||||
```
|
||||
### Access Token Reset
|
||||
> Regenerate the public access URL for the APP
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/site/access-token-reset`
|
||||
- **Method**: `POST`
|
||||
- **Body Example**: `null`
|
||||
|
||||
## Application Statistics API
|
||||
### All Conversations
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/statistics/daily-conversations`
|
||||
- **Method**: `GET`
|
||||
- **URL Parameters**: `/apps/{uuid:app_id}/statistics/daily-conversations?start=2024-04-19%2016%3A28&end=2024-04-26%2016%3A28`
|
||||
|
||||
### Active Users
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/statistics/daily-end-users`
|
||||
- **Method**: `GET`
|
||||
- **URL Parameters**: `/apps/{uuid:app_id}/statistics/daily-end-users?start=2024-04-19%2016%3A28&end=2024-04-26%2016%3A28`
|
||||
|
||||
### Token Costs
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/statistics/token-costs`
|
||||
- **Method**: `GET`
|
||||
- **URL Parameters**: `/apps/{uuid:app_id}/statistics/token-costs?start=2024-04-19%2016%3A28&end=2024-04-26%2016%3A28`
|
||||
|
||||
### Average Session Interactions
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/statistics/average-session-interactions`
|
||||
- **Method**: `GET`
|
||||
- **URL Parameters**: `/apps/{uuid:app_id}/statistics/average-session-interactions?start=2024-04-19%2016%3A28&end=2024-04-26%2016%3A28`
|
||||
|
||||
### User Satisfaction
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/statistics/user-satisfaction-rate`
|
||||
- **Method**: `GET`
|
||||
- **URL Parameters**: `/apps/{uuid:app_id}/statistics/user-satisfaction-rate?start=2024-04-19%2016%3A28&end=2024-04-26%2016%3A28`
|
||||
|
||||
### Average Response Time
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/statistics/average-response-time`
|
||||
- **Method**: `GET`
|
||||
- **URL Parameters**: `/apps/{uuid:app_id}/statistics/average-response-time?start=2024-04-19%2016%3A28&end=2024-04-26%2016%3A28`
|
||||
|
||||
### Token Output Speed
|
||||
- **Request**
|
||||
- **URL**: `/apps/{uuid:app_id}/statistics/tokens-per-second`
|
||||
- **Method**: `GET`
|
||||
- **URL Parameters**: `/apps/{uuid:app_id}/statistics/tokens-per-second?start=2024-04-19%2016%3A28&end=2024-04-26%2016%3A28`
|
||||
33
docs/developer/develop/advanced/cli/olares-change-ip.md
Normal file
33
docs/developer/develop/advanced/cli/olares-change-ip.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# `olares change-ip`
|
||||
:::warning When manually updating IP address is required
|
||||
When Olares is deployed _inside_ a virtualized environment, such as macOS (via Minikube) or Windows (via WSL), a change in the host system's IP address (e.g., due to switching Wi-Fi networks) may cause Olares to become inaccessible. This happens because the NAT gateway and DNS configuration no longer match the new IP. In such cases, you need to manually update the IP address to ensure that Olares can route traffic correctly.
|
||||
:::
|
||||
|
||||
## Synopsis
|
||||
Change the local IP address for all Olares components.
|
||||
|
||||
```bash
|
||||
olares-cli olares change-ip [option]
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Name | Shorthand | Usage |
|
||||
|------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--base-dir` | `-b` | Sets the base directory for Olares.<br> Defaults to `$HOME/.olares`. |
|
||||
| `--distribution` | `-d` | Sets the WSL distribution name. Only applicable on Windows. <br> Defaults to `Ubuntu`. |
|
||||
| `--help` | `-h` | Displays help information. |
|
||||
| `--profile` | `-p` | Sets the Minikube profile name. Only applicable on macOS. <br> Defaults to `olares-0`. |
|
||||
| `--version` | `-v` | Specifies the Olares version. <br>Version values follow the format `x.y.z` (e.g., `1.10.0`) or include a build date (e.g., `1.10.0-20241109`).<br> Refer to the [GitHub Releases page](https://github.com/beclab/Olares/releases) for available versions. |
|
||||
|
||||
## Examples
|
||||
- For macOS:
|
||||
```bash
|
||||
# Specify the Minikube profile name and change the IP.
|
||||
olares-cli olares change-ip --profile olares-0
|
||||
```
|
||||
- For Windows WSL:
|
||||
```bash
|
||||
# Specify the Linux distribution in WSL and change the IP.
|
||||
olares-cli olares change-ip --distribution Ubuntu
|
||||
```
|
||||
50
docs/developer/develop/advanced/cli/olares-cli.md
Normal file
50
docs/developer/develop/advanced/cli/olares-cli.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
---
|
||||
# Olares CLI
|
||||
|
||||
Olares provides Olares CLI, a command-line tool for developers and system administrators to customize or troubleshoot the Olares installation process.
|
||||
|
||||
The recommended [one-liner installation command](../../../../manual/get-started/install-olares.md) retrieves a shell script from https://olares.sh/ that downloads and installs Olares CLI. Once installed, the CLI orchestrates the remainder of the setup.
|
||||
|
||||
In general, Olares CLI manages installation through three main phases:
|
||||
1. **Download**: Olares CLI fetches the necessary components.
|
||||
2. **Prepare**: Olares CLI prepares the environment for installation.
|
||||
3. **Install**: Olares CLI installs the core services of Olares.
|
||||
|
||||
This page explains the Olares CLI syntax and describes the command operations.
|
||||
|
||||
:::info Root privileges required
|
||||
Most `olares-cli` commands require root privileges. Use the root user or prepend commands with `sudo`.
|
||||
:::
|
||||
|
||||
## Syntax
|
||||
The Olares CLI uses the following syntax:
|
||||
|
||||
> `olares-cli command [subcommand] [option]`
|
||||
|
||||
where
|
||||
- `command`: Specifies the main operation you want to perform. For example, `olares download`.
|
||||
- `subcommand`: Further specifies the task of `command`. For example, `wizard` or `component`.
|
||||
- `option`: Optional arguments that modify the behavior of the `command`. Options include flags and options with arguments.
|
||||
|
||||
Olares CLI allows you to temporarily override certain Olares default settings. Each option applies only to the command in which it is used.
|
||||
|
||||
For example, if you use the `--base-dir` option with `olares-cli olares download wizard`, it will only affect the wizard downloading process and will not change the base directory for other commands, such as during the "install" phase.
|
||||
|
||||
To get detailed help for any command, run `olares-cli help`.
|
||||
|
||||
## Available CLI commands
|
||||
|
||||
| Operation | Syntax | Description |
|
||||
|--------------------|----------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `olares info` | `olares-cli olares info [option]` | Displays general information about the downloaded Olares OS. |
|
||||
| `olares download` | `olares-cli olares download [subcommand] [option]` | Downloads specific resources. |
|
||||
| `olares prepare` | `olares-cli olares prepare [option]` | Prepares the environment for the installation process, including setting up essential services and configurations of Olares. |
|
||||
| `olares install` | `olares-cli olares install [option]` | Deploys system-level and user-level components of Olares. |
|
||||
| `olares change-ip` | `olares-cli olares change-ip [option]` | Changes the IP address of the Olares OS. |
|
||||
| `olares release` | `olares-cli olares release [option]` | Packages Olares installation resources for distribution or deployment. |
|
||||
| `olares uninstall` | `olares-cli olares uninstall [option]` | Uninstalls Olares completely, or roll back the installation to a specific phase. |
|
||||
|
||||
|
||||
|
||||
46
docs/developer/develop/advanced/cli/olares-download.md
Normal file
46
docs/developer/develop/advanced/cli/olares-download.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# `olares download`
|
||||
|
||||
## Synopsis
|
||||
The `olares download` command downloads the necessary packages and components required to install Olares on your local machine. It supports downloading components, checking the status of installation packages, and fetching the manifest file.
|
||||
|
||||
```bash
|
||||
olares-cli olares download [subcommand] [option]
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
| Name | Shorthand | Usage | Example |
|
||||
|-------------|-----------|-------------------------------------------------------|----------------------------------------|
|
||||
| `check` | | Checks the status of the Olares installation package. | `olares-cli olares download check` |
|
||||
| `component` | `c` | Downloads Olares components. | `olares-cli olares download component` |
|
||||
| `wizard` | `w` | Downloads the manifest file. | `olares-cli olares download wizard` |
|
||||
|
||||
## Options
|
||||
|
||||
| Name | Shorthand | Usage |
|
||||
|----------------------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--base-dir` | `-b` | Sets the base directory for Olares.<br> Defaults to `$HOME/.olares`. |
|
||||
| `--download-cdn-url` | | Sets the CDN accelerated download URL in the format `https://example.cdn.com`. <br>If not provided, the default URL will be used. |
|
||||
| `--help` | `-h` | Displays help information. |
|
||||
| `--kube` | | Specifies the Kubernetes type. <br>Supported types are `k3s` (default) and `k8s`. |
|
||||
| `--version` | `-v` | Specifies the Olares version. <br>Version values follow the format `x.y.z` (e.g., `1.10.0`) or include a build date (e.g., `1.10.0-20241109`).<br> Refer to the [GitHub Releases page](https://github.com/beclab/Olares/releases) for available versions. |
|
||||
|
||||
## Examples
|
||||
```bash
|
||||
# Specifies the base directory where all downloaded components will be stored.
|
||||
olares-cli olares download component -b /custom/path
|
||||
|
||||
# Specifies a CDN-accelerated URL for downloading Olares components.
|
||||
olares-cli olares download component --download-cdn-url https://my.cdn.com
|
||||
|
||||
# Specifies the Kubernetes type for the installation.
|
||||
olares-cli olares download component --kube k8s
|
||||
|
||||
# Sets the path to the package manifest file.
|
||||
olares-cli olares download component --manifest /custom/path/manifest.json
|
||||
|
||||
# Specifies the version of Olares packages and components to download.
|
||||
olares-cli olares download component -v 1.11.0
|
||||
```
|
||||
|
||||
|
||||
14
docs/developer/develop/advanced/cli/olares-info.md
Normal file
14
docs/developer/develop/advanced/cli/olares-info.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# `olares info`
|
||||
|
||||
## Synopsis
|
||||
The `olares info` command displays general information about the installed Olares version.
|
||||
|
||||
```bash
|
||||
olares-cli olares info
|
||||
```
|
||||
|
||||
## Flags
|
||||
|
||||
| Name | Shorthand | Usage |
|
||||
|----------|-----------|---------------------------|
|
||||
| `--help` | `-h` | Display help information. |
|
||||
19
docs/developer/develop/advanced/cli/olares-install.md
Normal file
19
docs/developer/develop/advanced/cli/olares-install.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# `olares install`
|
||||
|
||||
## Synopsis
|
||||
The `olares install` command installs Olares on your system. It supports various options to customize the installation process, such as specifying directories, Kubernetes types, or Minikube profiles.
|
||||
|
||||
```bash
|
||||
olares-cli olares install [option]
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Name | Shorthand | Usage |
|
||||
|--------------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--base-dir` | `-b` | Sets the base directory for Olares.<br> Defaults to `$HOME/.olares`. |
|
||||
| `--help` | `-h` | Displays help information. |
|
||||
| `--kube` | | Specifies the Kubernetes type. <br>Supported types are `k3s` (default) and `k8s`. |
|
||||
| `--profile` | `-p` | Sets the Minikube profile name. Only applicable on macOS. <br> Defaults to `olares-0`. |
|
||||
| `--version` | `-v` | Specifies the Olares version. <br>Version values follow the format `x.y.z` (e.g., `1.10.0`) or include a build date (e.g., `1.10.0-20241109`).<br> Refer to the [GitHub Releases page](https://github.com/beclab/Olares/releases) for available versions. |
|
||||
|
||||
25
docs/developer/develop/advanced/cli/olares-prepare.md
Normal file
25
docs/developer/develop/advanced/cli/olares-prepare.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# `olares prepare`
|
||||
|
||||
## Synopsis
|
||||
The `olares prepare` command sets up the environment required for Olares to function. This includes installing dependencies, importing container images, and starting the Olares daemon (`olaresd`).
|
||||
```bash
|
||||
olares-cli olares prepare [option]
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Name | Shorthand | Usage |
|
||||
|----------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--base-dir` | `-b` | Sets the base directory for Olares.<br> Defaults to `$HOME/.olares`. |
|
||||
| `--help` | `-h` | Displays help information. |
|
||||
| `--kube` | | Specifies the Kubernetes type. <br>Supported types are `k3s` (default) and `k8s`. |
|
||||
| `--profile` | `-p` | Sets the Minikube profile name. Only applicable on macOS. <br> Defaults to `olares-0`. |
|
||||
| `--registry-mirrors` | `-r` | Specifies Docker container registry mirrors. <br> Multiple mirrors should be separated by commas. |
|
||||
| `--with-juicefs` | | Configures JuiceFS as the root filesystem (rootfs) for Olares workloads instead of the local disk. |
|
||||
| `--version` | `-v` | Specifies the Olares version. <br>Version values follow the format `x.y.z` (e.g., `1.10.0`) or include a build date (e.g., `1.10.0-20241109`).<br> Refer to the [GitHub Releases page](https://github.com/beclab/Olares/releases) for available versions. |
|
||||
|
||||
## Example
|
||||
```bash
|
||||
# Uses JuiceFS as the root filesystem
|
||||
olares-cli olares prepare --with-juicefs=true
|
||||
```
|
||||
20
docs/developer/develop/advanced/cli/olares-release.md
Normal file
20
docs/developer/develop/advanced/cli/olares-release.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# `olares release`
|
||||
|
||||
## Synopsis
|
||||
Build a release version based on a local Olares repository. This command should be run in the root directory of the Olares repository.
|
||||
|
||||
```bash
|
||||
olares-cli olares release [option]
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Name | Shorthand | Usage |
|
||||
|----------------------------|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--base-dir` | `-b` | Sets the base directory for Olares.<br> Defaults to `$HOME/.olares`. |
|
||||
| `--download-cdn-url` | | Sets the CDN URL used for downloading checksums of dependencies and images. <br> Defaults to `https://dc3p1870nn3cj.cloudfront.net`. |
|
||||
| `--extract` | `-e` | Extracts the release to the `--base-dir` after the build. Set to `false` if only the release file itself is needed. <br> Defaults to `true`. |
|
||||
| `--help` | `-h` | Displays help information. |
|
||||
| `---ignore-missing-images` | | Ignores missing images when downloading checksums from the CDN. <br> Disable this only if no new images are added, as the build may fail if the image is not uploaded to the CDN yet. <br> Defaults to `true`. |
|
||||
| `--version` | `-v` | Specifies the Olares version. <br>Version values follow the format `x.y.z` (e.g., `1.10.0`) or include a build date (e.g., `1.10.0-20241109`).<br> Refer to the [GitHub Releases page](https://github.com/beclab/Olares/releases) for available versions. |
|
||||
|
||||
19
docs/developer/develop/advanced/cli/olares-uninstall.md
Normal file
19
docs/developer/develop/advanced/cli/olares-uninstall.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# `olares uninstall`
|
||||
|
||||
## Synopsis
|
||||
Uninstall Olares from your machine.
|
||||
|
||||
```bash
|
||||
olares-cli olares uninstall [option]
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Name | Shorthand | Usage |
|
||||
|--------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--all` | | Uninstalls Olares completely, including dependencies installed during the "prepare" phase. |
|
||||
| `--base-dir` | `-b` | Sets the base directory for Olares.<br> Defaults to `$HOME/.olares`. |
|
||||
| `--help` | `-h` | Displays help information. |
|
||||
| `--phase` | | Uninstalls Olares from a specific phase and revert to the previous one. <br> For example, `--phase install` removes tasks performed during the "install" phase, reverting the system to the "prepare" stage. <br>Defaults to `install`. |
|
||||
| `--quiet` | | Enables quiet mode (suppress output). <br> Defaults to `false`. |
|
||||
| `--version` | `-v` | Specifies the Olares version to uninstall. <br>Use `olares-cli olares info` to check the downloaded version first. |
|
||||
26
docs/developer/develop/advanced/cookie.md
Normal file
26
docs/developer/develop/advanced/cookie.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Cookie
|
||||
|
||||
**Single Sign-On (SSO)** mode is utilized for authorization and authentication across the **Olares**, including all installed apps. **SSO** authentication is non-intrusive, using cookies as the authentication credential.
|
||||
|
||||
The system will set two cookies after login
|
||||
|
||||
- **authelia_session**
|
||||
|
||||
The content of the cookie is the session id of SSO. The scope is the user's Olares domain, `<username>.olares.com`
|
||||
|
||||
- **auth_token**
|
||||
|
||||
The user authenticated authorization token. The scope is the user's Olares domain, `<username>.olares.com`
|
||||
|
||||
To prevent cookie conflicts, **no application** (whether it's a built-in system app or a third-party app) can set cookies to the user's domain. Cookies can only be set to the domain of the app.
|
||||
|
||||
Every application in **Olares** operates under two domains: <`app id>.<username>.olares.com` and `<app id>.local.<username>.olares.com`. As a result, Olares incorporates a cookie-setting `rewrite` mechanism within the Olares Application Runtime. This ensures that the application automatically assigns cookies for both domains in the Set-`Cookie` field of the **HTTP response**.
|
||||
|
||||
To use this feature, you just need to define it in the application chart's [OlaresManifest.yaml](../package/manifest.md#resetcookie)
|
||||
|
||||
```yaml
|
||||
options:
|
||||
resetCookie:
|
||||
enabled: true
|
||||
|
||||
```
|
||||
65
docs/developer/develop/advanced/database.md
Normal file
65
docs/developer/develop/advanced/database.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Database
|
||||
|
||||
The Olares system provides three most popular data storage cluster for all APPs, covering `RDS`, `NoSQL`, and `Cache` data storage use cases.
|
||||
|
||||
## RDS
|
||||
|
||||
The system has deployed **PostgreSQL** and provides two types of databases.
|
||||
|
||||
- **Standalone PostgreSQL**, providing the most commonly used `RDS` database layer functions.
|
||||
- **Distributed PostgreSQL** extension, powered by **Citus**. Provides the ability to horizontally scale the database.
|
||||
|
||||
When setting up a **PostgreSQL** database, you can specify the type of database to be used in [OlaresManifest.yaml](../package/manifest.md#middleware).
|
||||
|
||||
```yaml
|
||||
middleware:
|
||||
postgres:
|
||||
username: postgres
|
||||
databases:
|
||||
- name: db
|
||||
distributed: true # Whether the database is distributed in the cluster.
|
||||
```
|
||||
|
||||
If you use **Citus**, **Olares** will automatically shard the database tables and perform rebalancing during the horizontal scaling of **PostgreSQL** replicas.
|
||||
|
||||
## NoSQL
|
||||
|
||||
The NoSQL cluster is not deployed by default in Olares, but it can be easily installed from the Market. To set up a NoSQL cluster, the administrator needs to install the [**MongoDB**](https://market.olares.com/middleware/mongodb) middleware. Once installed, the [Percona Operator for MongoDB](https://github.com/percona/percona-server-mongodb-operator) automatically manages the **MongoDB** cluster. Users can then horizontally scale **MongoDB** cluster replicas, as well as perform backup and restore operations on databases.
|
||||
|
||||
You can specify detailed configuration for MongoDB in [OlaresManifest.yaml](../package/manifest.md#middleware) as follows:
|
||||
|
||||
```yaml
|
||||
middleware:
|
||||
mongodb:
|
||||
username: mongodb
|
||||
databases:
|
||||
- name: db0
|
||||
- name: db1
|
||||
options:
|
||||
dependencies:
|
||||
- name: olares
|
||||
type: system
|
||||
version: ">=1.6.0-0"
|
||||
- name: mongodb
|
||||
version: ">=6.0.0-0"
|
||||
type: middleware
|
||||
```
|
||||
|
||||
## Cache
|
||||
|
||||
In terms of the Cache cluster, Olares uses Redis Cluster. The cluster is managed by a customized [Redis Cluster Operator](https://github.com/beclab/redis-cluster-operator) to achieve cloud nativeness. It enables us to scale replicas horizontally in a convenient and effective manner.
|
||||
|
||||
To ensure **data isolation** between users and apps in the **Redis cluster**, the **Olares** system has added a **Redis cluster proxy**. It isolates data based on the `namespace`. This operation is transparent, meaning app developers typically do not need to be aware of it.
|
||||
|
||||
Additionally, this proxy simplifies the process of connecting to clusters. It eliminates the need to switch from a **standalone Redis Client** to a **Redis Cluster client** in the app, thus simplifying app code modifications.
|
||||
|
||||
```
|
||||
middleware:
|
||||
redis:
|
||||
password: password
|
||||
namespace: db0
|
||||
```
|
||||
|
||||
:::info NOTE
|
||||
Since Olares uses the Redis Cluster version, developers need to understand the usage restrictions of Redis Cluster in detail when using it.
|
||||
:::
|
||||
175
docs/developer/develop/advanced/file-upload.md
Normal file
175
docs/developer/develop/advanced/file-upload.md
Normal file
@@ -0,0 +1,175 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
---
|
||||
|
||||
# File Upload
|
||||
|
||||
There are many situations where you might need to upload local files to your edge when using **Olares**. The `Olares Application Runtime` provides a common file-upload component to simplify this process in app development. Moreover, this file-upload component features **resumable upload**.
|
||||
|
||||
## How to install
|
||||
|
||||
To use this feature, simply add the following configuration to the [OlaresManifest.yaml](../package/manifest.md#upload) file in the application chart.
|
||||
```yaml
|
||||
upload:
|
||||
fileType:
|
||||
- pdf
|
||||
dest: /appdata
|
||||
limitedSize: 3729747942
|
||||
```
|
||||
|
||||
## Frontend API
|
||||
|
||||
:::info NOTE
|
||||
The limit for a single upload is 10MB. If the file is larger than 10MB, you must upload using the resumable upload API.
|
||||
:::
|
||||
|
||||
|
||||
### Upload
|
||||
|
||||
This interface is used to upload files to the server and get the file id and status.
|
||||
:::details Example
|
||||
**Request**
|
||||
```sh
|
||||
curl --location 'http://host:40030/upload/' \
|
||||
--form 'storage_path="./testupload/"' \
|
||||
--form 'file_relative_path="1.csv"' \
|
||||
--form 'file_type="csv"' \
|
||||
--form 'file_size="1937"'
|
||||
```
|
||||
**Response**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"id": "b0b76f02bdb8ee3269602c983c4a2aeb",
|
||||
"offset": 0,
|
||||
"file_relative_path": "1.csv",
|
||||
"file_type": "csv",
|
||||
"file_size": 1937,
|
||||
"storage_path": "./testupload/"
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
- **Request**
|
||||
|
||||
- **URL**: `/upload/`
|
||||
|
||||
- **Method**: `POST`
|
||||
|
||||
- **Body**:
|
||||
``` json
|
||||
"mode": "formdata", //The request body type must set to multipart/form-data
|
||||
"body parameters": {
|
||||
"storage_path": string, //required, the storage path for the file on the server. Ensure that this folder exists.
|
||||
"file_relative_path": string, //required, the location of file relative to the storage_path, it must include the filename. If it is a floder, end with '/'
|
||||
"file_type": string, //required, File type
|
||||
"file_size": integer, //required, File size
|
||||
}
|
||||
```
|
||||
- **Success Response**
|
||||
|
||||
The response body contains the following content, formatted in JSON.
|
||||
- **Status Code** : `200 OK`
|
||||
```json
|
||||
"code": integer, // Response code, 0 means success, non-zero means failure.
|
||||
"message": string, // Response message, return "success" upon success, and the corresponding error message upon failure.
|
||||
"data":{ //Response data. Upon success, it includes the following fields (these contents is absent when uploading a folder):
|
||||
"id": string, // Unique identifier of the file
|
||||
"offset": integer, //The offset of the file uploaded
|
||||
"file_relative_path": string,
|
||||
"file_type": string,
|
||||
"file_size": integer,
|
||||
"storage_path": string
|
||||
}
|
||||
```
|
||||
|
||||
- **Error Response**
|
||||
- **Status Code** : `400 Bad Request`
|
||||
> The request is invalid due to illegal or missing parameters.
|
||||
- **Status Code** : `500 Internal Server Error`
|
||||
> An internal server error has occurred, which prevented the request from being fulfilled. This could be due to reasons such as failure to create a folder or save file information.
|
||||
|
||||
### Resumable Upload
|
||||
|
||||
This interface is used to continue uploading the remaining part of the file.
|
||||
:::details Example
|
||||
**Request**
|
||||
```sh
|
||||
curl --location --request PATCH 'http://host:40030/upload/b0b76f02bdb8ee3269602c983c4a2aeb' \
|
||||
--form 'file=@"/Users/yangtao/Downloads/1.csv"' \
|
||||
--form 'upload_offset="0"'
|
||||
```
|
||||
**Response**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "File uploaded successfully",
|
||||
"data": {
|
||||
"id": "b0b76f02bdb8ee3269602c983c4a2aeb",
|
||||
"offset": 1937,
|
||||
"file_name": "1.csv",
|
||||
"file_type": "csv",
|
||||
"file_size": 1937,
|
||||
"storage_path": "./testupload"
|
||||
}
|
||||
}
|
||||
{
|
||||
"code": 0,
|
||||
"message": "Continue uploading",
|
||||
"data": {
|
||||
"id": "e3133b0f838124ff3ebcc9cb14774f26",
|
||||
"offset": 1048576,
|
||||
"file_name": "1.pdf",
|
||||
"file_type": ".pdf",
|
||||
"file_size": 10296258,
|
||||
"storage_path": "./testupload"
|
||||
}
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
- **Request**
|
||||
|
||||
- **URL**: `http://host:40030/upload/{uid}`
|
||||
|
||||
- **Method**: `PATCH`
|
||||
|
||||
- **Body**:
|
||||
``` json
|
||||
"mode": "formdata", //The request body type must set to multipart/form-data
|
||||
"body parameters": {
|
||||
"file": string, //required. The file to be uploaded. Please upload the file in multipart/form-data format.
|
||||
"upload_offset": integer, //required. The offset refers to the size of the file that has already been uploaded.
|
||||
}
|
||||
"url parameters": {
|
||||
"uid": string, //required. This is the unique identifier of the file. You can obtain it from the response data of the Upload API.
|
||||
}
|
||||
```
|
||||
- **Success Response**
|
||||
|
||||
The response body contains the following content, formatted in JSON.
|
||||
- **Status Code** : `200 OK`
|
||||
```json
|
||||
"code": integer, // Response code, 0 means success, non-zero means failure.
|
||||
"message": string, // Response message, return "File uploaded successfully" upon success, and the corresponding error message upon failure.
|
||||
"data":{ //Response data. Upon success, it includes the following fields
|
||||
"id": string, // Unique identifier of the file
|
||||
"offset": integer, //The offset of the file uploaded
|
||||
"file_relative_path": string,
|
||||
"file_type": string,
|
||||
"file_size": integer,
|
||||
"storage_path": string
|
||||
}
|
||||
```
|
||||
|
||||
- **Error Response**
|
||||
- **Status Code** : `400 Bad Request`
|
||||
> The request is invalid due to illegal or missing parameters.
|
||||
```json
|
||||
{ "code": 1, "message": "Invalid upload ID" }
|
||||
```
|
||||
- **Status Code** : `500 Internal Server Error`
|
||||
> An internal server error has occurred, which prevented the request from being fulfilled. This could be due to reasons such as failure to create a folder, save file information, or move file.
|
||||
9
docs/developer/develop/advanced/frontend.md
Normal file
9
docs/developer/develop/advanced/frontend.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Frontend
|
||||
|
||||
## 视觉规范
|
||||
|
||||
https://w8wvyn24o5.feishu.cn/wiki/Im1nwwPCRiIcGCkBy8qc1vNknVd
|
||||
|
||||
## core 库
|
||||
|
||||
## UI 库
|
||||
BIN
docs/developer/develop/advanced/image-6.png
Normal file
BIN
docs/developer/develop/advanced/image-6.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
BIN
docs/developer/develop/advanced/images/restore.jpg
Normal file
BIN
docs/developer/develop/advanced/images/restore.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 240 KiB |
14
docs/developer/develop/advanced/index.md
Normal file
14
docs/developer/develop/advanced/index.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Advanced
|
||||
|
||||
1. [Olares CLI](cli/olares-cli.md)
|
||||
2. [Olares Info](./terminus-info)
|
||||
3. [Service Provider](./provider.md)
|
||||
4. [AI](./ai.md)
|
||||
5. [Cookie](./cookie.md)
|
||||
6. [Database](./database.md)
|
||||
7. [Account](./account.md)
|
||||
8. [Market](./market.md)
|
||||
9. [Websocket](./websocket.md)
|
||||
10. [Files Upload](./file-upload.md)
|
||||
11. [Secret](./secret.md)
|
||||
12. [Kubesphere](./kubesphere.md)
|
||||
14
docs/developer/develop/advanced/kubesphere.md
Normal file
14
docs/developer/develop/advanced/kubesphere.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Kubesphere
|
||||
|
||||
Olares has integrated many advanced features of Kubesphere like the multi-user system and cluster data monitoring. To install the official console tool from **Kubesphere**, download and install it from the **Olares** code repository.
|
||||
|
||||
```sh
|
||||
curl -LO https://github.com/Above-Os/terminus-os/raw/main/third-party/ks-console/ks-console-v3.3.0.tgz
|
||||
|
||||
# The username is your Olares ID
|
||||
sudo helm install console ./ks-console-v3.3.0.tgz \
|
||||
-n user-space-<username> \
|
||||
--set username=<username>
|
||||
```
|
||||
|
||||
After you install it, refresh your desktop. You'll then see the Console icon in Olares. Open Console and log in with your Olares ID and password.
|
||||
79
docs/developer/develop/advanced/market.md
Normal file
79
docs/developer/develop/advanced/market.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Market
|
||||
|
||||
To install or uninstall apps in developing applications (for example, third-party Market extensions), developers can use the `install` and `uninstall` API provided by the **Market**.
|
||||
|
||||
For how to define and call provider, please refer to [Service Provider](./provider.md)
|
||||
|
||||
**Provider from Market**
|
||||
|
||||
| Group | version | dataType | ops |
|
||||
| ---------------- | ------- | -------- | ----------------------------- |
|
||||
| service.appstore | v1 | app | `InstallDevApp`<br>`UninstallDevApp` |
|
||||
|
||||
## Install
|
||||
- **Request**
|
||||
- **URL**: <br>`http://$OS_SYSTEM_SERVER/system-server/v1alpha1/app/service.appstore/v1/InstallDevApp`
|
||||
|
||||
- **Method**: `POST`
|
||||
|
||||
- **Header**
|
||||
```http
|
||||
X-Authorization: token # auth_token in cookie
|
||||
X-Access-Token: access_token # access token get from authorization Provider
|
||||
```
|
||||
|
||||
- **Body** (Golang struct as an example)
|
||||
```go
|
||||
type InstallOptions struct {
|
||||
App string `json:"appName"` //required
|
||||
RepoUrl string `json:"repoUrl"` //required
|
||||
CfgUrl string `json:"cfgUrl"` //optional
|
||||
Version string `json:"version"` //required when upgrading
|
||||
Source string `json:"source"` //required
|
||||
}
|
||||
```
|
||||
|
||||
- **Success Responses**
|
||||
```go
|
||||
type InstallationResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"message,omitempty"`
|
||||
Data InstallationResponseData `json:"data"`
|
||||
}
|
||||
|
||||
type InstallationResponseData struct {
|
||||
UID string `json:"uid"`
|
||||
}
|
||||
```
|
||||
|
||||
## Uninstall
|
||||
- **Request**
|
||||
- **URL**: <br>`http://$OS_SYSTEM_SERVER/system-server/v1alpha1/app/service.appstore/v1/UninstallDevApp`
|
||||
|
||||
- **Method**: `POST`
|
||||
|
||||
- **Header**
|
||||
```http
|
||||
X-Authorization: token # auth_token in cookie
|
||||
X-Access-Token: access_token # access token get from authorization Provider
|
||||
```
|
||||
|
||||
- **Body** (Golang struct as an example)
|
||||
```go
|
||||
type UninstallData struct {
|
||||
Name string `json:"name"` //required
|
||||
}
|
||||
```
|
||||
|
||||
- **Success Responses**
|
||||
```go
|
||||
type InstallationResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"message,omitempty"`
|
||||
Data InstallationResponseData `json:"data"`
|
||||
}
|
||||
|
||||
type InstallationResponseData struct {
|
||||
UID string `json:"uid"`
|
||||
}
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user