Compare commits
13 Commits
module-bfl
...
cli/fix/ig
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f16ec6b0c6 | ||
|
|
7404674a20 | ||
|
|
f116970ad0 | ||
|
|
b6e866ce75 | ||
|
|
39bd546ac8 | ||
|
|
5820b5612e | ||
|
|
ef78e21933 | ||
|
|
8ce8b6c976 | ||
|
|
cbab40a597 | ||
|
|
14691ea3ec | ||
|
|
98f123fbf1 | ||
|
|
8f5023ce17 | ||
|
|
4467bc61df |
@@ -3,10 +3,10 @@
|
||||
# Olares: An Open-Source Personal Cloud to </br>Reclaim Your Data<!-- omit in toc -->
|
||||
|
||||
[](#)<br/>
|
||||
[](https://github.com/beclab/olares/commits/main)
|
||||
[](https://github.com/beclab/olares/commits/main)
|
||||

|
||||
[](https://github.com/beclab/olares/releases)
|
||||
[](https://github.com/beclab/olares/stargazers)
|
||||
[](https://github.com/beclab/olares/releases)
|
||||
[](https://github.com/beclab/Olares/stargazers)
|
||||
[](https://discord.gg/olares)
|
||||
[](https://github.com/beclab/olares/blob/main/LICENSE)
|
||||
|
||||
@@ -45,7 +45,7 @@ Just as Public clouds offer IaaS, PaaS, and SaaS layers, Olares provides open-so
|
||||
|
||||

|
||||
|
||||
For detailed description of each component, refer to [Olares architecture](https://docs.olares.com/manual/concepts/system-architecture.html).
|
||||
For detailed description of each component, refer to [Olares architecture](https://docs.olares.com/developer/concepts/system-architecture.html).
|
||||
|
||||
> 🔍 **How is Olares different from traditional NAS?**
|
||||
>
|
||||
|
||||
@@ -317,7 +317,7 @@ spec:
|
||||
chown -R 1000:1000 /uploadstemp && \
|
||||
chown -R 1000:1000 /appdata
|
||||
- name: olares-app-init
|
||||
image: beclab/system-frontend:v1.9.9
|
||||
image: beclab/system-frontend:v1.9.12
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /bin/sh
|
||||
|
||||
@@ -159,6 +159,7 @@ const (
|
||||
CommandUpdatePciids = "update-pciids"
|
||||
CommandNmcli = "nmcli"
|
||||
CommandZRAMCtl = "zramctl"
|
||||
CommandChronyc = "chronyc"
|
||||
|
||||
CacheCommandKubectlPath = "kubectl_bin_path"
|
||||
CacheCommandMinikubePath = "minikube_bin_path"
|
||||
|
||||
@@ -512,7 +512,6 @@ func getCpu() *CpuInfo {
|
||||
if err == nil && strings.TrimSpace(string(output)) != "" {
|
||||
isGB10Chip = true
|
||||
} else {
|
||||
fmt.Printf("Error checking GB10 chip: %v\n", err)
|
||||
gb10env := os.Getenv(common.ENV_GB10_CHIP)
|
||||
if gb10env == "1" || strings.EqualFold(gb10env, "true") {
|
||||
isGB10Chip = true
|
||||
|
||||
@@ -116,8 +116,14 @@ func (m *CheckPreparedModule) Init() {
|
||||
Action: &CheckPrepared{Force: m.Force},
|
||||
}
|
||||
|
||||
checkTimeSync := &task.LocalTask{
|
||||
Name: "CheckTimeSynced",
|
||||
Action: &WaitTimeSyncTask{},
|
||||
}
|
||||
|
||||
m.Tasks = []task.Interface{
|
||||
checkPrepared,
|
||||
checkTimeSync,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1033,3 +1033,37 @@ func (a *SaveMasterHostConfig) Execute(runtime connector.Runtime) error {
|
||||
}
|
||||
return os.WriteFile(filepath.Join(runtime.GetBaseDir(), common.MasterHostConfigFile), content, 0644)
|
||||
}
|
||||
|
||||
type WaitTimeSyncTask struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *WaitTimeSyncTask) Execute(runtime connector.Runtime) error {
|
||||
if chronyc, err := util.GetCommand(common.CommandChronyc); err == nil && chronyc != "" {
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
timeout := time.NewTimer(5 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
defer timeout.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// output format:
|
||||
// 68839BAF,104.131.155.175,3,1772592384.619310832,-0.001840593,0.001674238,0.001874871,-5.194,-0.001,0.112,0.162520304,0.010412607,1035.0,Normal
|
||||
if res, err := runtime.GetRunner().Cmd(fmt.Sprintf("%s -c tracking", chronyc), false, true); err != nil {
|
||||
logger.Errorf("failed to execute chronyc tracking: %v", err)
|
||||
return err
|
||||
} else {
|
||||
resToken := strings.Split(res, ",")
|
||||
// if the stratum of the server is 10 which means the local reference (hardware RTC) is active.
|
||||
if strings.ToLower(resToken[2]) != "10" { // Stratum
|
||||
logger.Infof("time synchronization is normal")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
case <-timeout.C:
|
||||
return fmt.Errorf("timeout waiting for time synchronization")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func WithSerial(ctx context.Context, serial string) context.Context {
|
||||
}
|
||||
|
||||
func (w *usbWatcher) Watch(ctx context.Context) {
|
||||
retry := 1
|
||||
retry := 3
|
||||
devs, err := utils.DetectdUsbDevices(ctx)
|
||||
for {
|
||||
if err != nil {
|
||||
|
||||
@@ -43,11 +43,12 @@ type state struct {
|
||||
Disk string `json:"disk"`
|
||||
|
||||
// network info
|
||||
WikiConnected bool `json:"wifiConnected"`
|
||||
WifiSSID *string `json:"wifiSSID,omitempty"`
|
||||
WiredConnected bool `json:"wiredConnected"`
|
||||
HostIP string `json:"hostIp"`
|
||||
ExternalIP string `json:"externalIp"`
|
||||
WikiConnected bool `json:"wifiConnected"`
|
||||
WifiSSID *string `json:"wifiSSID,omitempty"`
|
||||
WiredConnected bool `json:"wiredConnected"`
|
||||
HostIP string `json:"hostIp"`
|
||||
ExternalIP string `json:"externalIp"`
|
||||
ExternalIPProbeTime time.Time `json:"-"`
|
||||
|
||||
// installing / uninstalling / upgrading state
|
||||
InstallingState ProcessingState `json:"installingState"`
|
||||
@@ -130,7 +131,8 @@ func CheckCurrentStatus(ctx context.Context) error {
|
||||
klog.Info("current state: ", CurrentState.TerminusState)
|
||||
}()
|
||||
|
||||
utils.ForceMountHdd(ctx)
|
||||
// Deprecated, only for Olares Zero
|
||||
// utils.ForceMountHdd(ctx)
|
||||
|
||||
// set default value
|
||||
if CurrentState.TerminusVersion == nil {
|
||||
@@ -255,7 +257,10 @@ func CheckCurrentStatus(ctx context.Context) error {
|
||||
}
|
||||
|
||||
CurrentState.HostIP = hostIp
|
||||
CurrentState.ExternalIP = nets.GetMyExternalIPAddr()
|
||||
if time.Since(CurrentState.ExternalIPProbeTime) > 1*time.Minute {
|
||||
CurrentState.ExternalIP = nets.GetMyExternalIPAddr()
|
||||
CurrentState.ExternalIPProbeTime = time.Now()
|
||||
}
|
||||
|
||||
// get olares state
|
||||
|
||||
|
||||
@@ -181,6 +181,7 @@ var (
|
||||
// {"installing k8s and kubesphere", "3%", 3},
|
||||
// {"Generating \"ca\" certificate and key", "3%", 3},
|
||||
// {"PatchKsCoreStatus success", "6%", 6},
|
||||
{"time synchronization is normal", "3%", 3},
|
||||
{"k8s and kubesphere installation is complete", "10%", 10},
|
||||
{"Installing account ...", "15%", 15},
|
||||
{"Installing settings ...", "20%", 20},
|
||||
|
||||
@@ -4,14 +4,17 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beclab/Olares/daemon/pkg/commands"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"github.com/libp2p/go-netroute"
|
||||
pkg_errors "github.com/pkg/errors"
|
||||
"github.com/txn2/txeh"
|
||||
@@ -267,15 +270,7 @@ func GetHostIpFromHostsFile(domain string) (string, error) {
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// GetMyExternalIPAddr get my network outgoing ip address
|
||||
func GetMyExternalIPAddr() string {
|
||||
sites := map[string]string{
|
||||
"httpbin": "https://httpbin.org/ip",
|
||||
"ifconfigme": "https://ifconfig.me/all.json",
|
||||
"externalip": "https://myexternalip.com/json",
|
||||
"joinolares": "https://myip.joinolares.cn/ip",
|
||||
}
|
||||
|
||||
type httpBin struct {
|
||||
Origin string `json:"origin"`
|
||||
}
|
||||
@@ -295,80 +290,80 @@ func GetMyExternalIPAddr() string {
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
var unmarshalFuncs = map[string]func(v []byte) string{
|
||||
"httpbin": func(v []byte) string {
|
||||
var hb httpBin
|
||||
if err := json.Unmarshal(v, &hb); err == nil && hb.Origin != "" {
|
||||
return hb.Origin
|
||||
}
|
||||
return ""
|
||||
},
|
||||
"ifconfigme": func(v []byte) string {
|
||||
var ifMe ifconfigMe
|
||||
if err := json.Unmarshal(v, &ifMe); err == nil && ifMe.IPAddr != "" {
|
||||
return ifMe.IPAddr
|
||||
}
|
||||
return ""
|
||||
},
|
||||
"externalip": func(v []byte) string {
|
||||
var extip externalIP
|
||||
if err := json.Unmarshal(v, &extip); err == nil && extip.IP != "" {
|
||||
return extip.IP
|
||||
}
|
||||
return ""
|
||||
},
|
||||
"joinolares": func(v []byte) string {
|
||||
return strings.TrimSpace(string(v))
|
||||
},
|
||||
type siteConfig struct {
|
||||
url string
|
||||
unmarshalFunc func(v []byte) string
|
||||
}
|
||||
|
||||
ch := make(chan any, len(sites))
|
||||
|
||||
for site := range sites {
|
||||
go func(name string) {
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
c := http.Client{Timeout: 5 * time.Second}
|
||||
resp, err := c.Get(sites[name])
|
||||
if err != nil {
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
|
||||
ip := unmarshalFuncs[name](respBytes)
|
||||
//println(name, site, ip)
|
||||
ch <- ip
|
||||
}(site)
|
||||
externalIPServiceURL, err := url.JoinPath(commands.OLARES_REMOTE_SERVICE, "/myip/ip")
|
||||
if err != nil {
|
||||
klog.Error("failed to parse external IP service URL, ", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
tr := time.NewTimer(time.Duration(5*len(sites)+3) * time.Second)
|
||||
|
||||
LOOP:
|
||||
for i := 0; i < len(sites); i++ {
|
||||
select {
|
||||
case r, ok := <-ch:
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch v := r.(type) {
|
||||
case string:
|
||||
ip := net.ParseIP(v)
|
||||
if ip != nil && ip.To4() != nil && !ip.IsLoopback() && !ip.IsMulticast() {
|
||||
return v
|
||||
sites := []siteConfig{
|
||||
{
|
||||
url: externalIPServiceURL,
|
||||
unmarshalFunc: func(v []byte) string {
|
||||
return strings.TrimSpace(string(v))
|
||||
},
|
||||
},
|
||||
{
|
||||
url: "https://httpbin.org/ip",
|
||||
unmarshalFunc: func(v []byte) string {
|
||||
var hb httpBin
|
||||
if err := json.Unmarshal(v, &hb); err == nil && hb.Origin != "" {
|
||||
return hb.Origin
|
||||
}
|
||||
case error:
|
||||
klog.Warningf("got an error, %v", v)
|
||||
}
|
||||
case <-tr.C:
|
||||
tr.Stop()
|
||||
klog.Warning("timed out")
|
||||
break LOOP
|
||||
return ""
|
||||
},
|
||||
},
|
||||
{
|
||||
url: "https://ifconfig.me/all.json",
|
||||
unmarshalFunc: func(v []byte) string {
|
||||
var ifMe ifconfigMe
|
||||
if err := json.Unmarshal(v, &ifMe); err == nil && ifMe.IPAddr != "" {
|
||||
return ifMe.IPAddr
|
||||
}
|
||||
return ""
|
||||
},
|
||||
},
|
||||
{
|
||||
url: "https://myexternalip.com/json",
|
||||
unmarshalFunc: func(v []byte) string {
|
||||
var extip externalIP
|
||||
if err := json.Unmarshal(v, &extip); err == nil && extip.IP != "" {
|
||||
return extip.IP
|
||||
}
|
||||
return ""
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
client := http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
}
|
||||
for _, site := range sites {
|
||||
resp, err := client.Get(site.url)
|
||||
if err != nil {
|
||||
log.Warnf("failed to get external ip from %s, %v", site.url, err)
|
||||
continue
|
||||
}
|
||||
|
||||
respBytes, readErr := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if readErr != nil {
|
||||
log.Warnf("failed to read response from %s, %v", site.url, readErr)
|
||||
continue
|
||||
}
|
||||
|
||||
ipStr := site.unmarshalFunc(respBytes)
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip != nil && ip.To4() != nil && !ip.IsLoopback() && !ip.IsMulticast() {
|
||||
return ipStr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -216,23 +216,23 @@ export const oneSidebar: DefaultTheme.Sidebar = {
|
||||
link: "/one/update",
|
||||
},
|
||||
{
|
||||
text: "Back up & restore",
|
||||
text: "Back up & restore data",
|
||||
link: "/one/backup-resotre",
|
||||
},
|
||||
{
|
||||
text: "Factory reset",
|
||||
text: "Restore Olares One",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Using LarePass",
|
||||
text: "Factory reset",
|
||||
link: "/one/factory-reset",
|
||||
},
|
||||
{
|
||||
text: "In BIOS",
|
||||
text: "Restore BIOS defaults",
|
||||
link: "/one/factory-reset-in-bios",
|
||||
},
|
||||
{
|
||||
text: "Using bootable USB",
|
||||
text: "Reinstall Olares OS",
|
||||
link: "/one/create-drive",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -216,23 +216,23 @@ export const oneSidebar: DefaultTheme.Sidebar = {
|
||||
link: "/zh/one/update",
|
||||
},
|
||||
{
|
||||
text: "Back up & restore",
|
||||
text: "Back up & restore data",
|
||||
link: "/zh/one/backup-resotre",
|
||||
},
|
||||
{
|
||||
text: "Factory reset",
|
||||
text: "Restore Olares One",
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Using LarePass",
|
||||
text: "Factory reset",
|
||||
link: "/zh/one/factory-reset",
|
||||
},
|
||||
{
|
||||
text: "In BIOS",
|
||||
text: "Restore BIOS defaults",
|
||||
link: "/zh/one/factory-reset-in-bios",
|
||||
},
|
||||
{
|
||||
text: "Using bootable USB",
|
||||
text: "Reinstall Olares OS",
|
||||
link: "/zh/one/create-drive",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -7,7 +7,15 @@ outline: [2, 3]
|
||||
Every **Olares Application Chart** should include an `OlaresManifest.yaml` file in the root directory. `OlaresManifest.yaml` provides all the essential information about an Olares App. Both the **Olares Market protocol** and the Olares depend on this information to distribute and install applications.
|
||||
|
||||
:::info NOTE
|
||||
Latest Olares Manifest version: `0.10.0`
|
||||
Latest Olares Manifest version: `0.11.0`
|
||||
- Removed deprecated fields of sysData
|
||||
- Updated the example of shared app
|
||||
- Added the apiVersion
|
||||
- Added the sharedEntrance section
|
||||
|
||||
:::
|
||||
:::details Changelog
|
||||
`0.10.0`
|
||||
- Modified the `categories` field
|
||||
- Added the `provider` field in the Permission section
|
||||
- Added the Provider section, to allow apps to expose specific service interfaces within the cluster
|
||||
@@ -15,8 +23,7 @@ Latest Olares Manifest version: `0.10.0`
|
||||
- Removed some deprecated fields from the Option section
|
||||
- Added the `allowMultipleInstall` field, allowing the app to be installed as multiple independent instances
|
||||
- Added the Envs section, to define environment variables required by the application
|
||||
:::
|
||||
:::details Changelog
|
||||
|
||||
`0.9.0`
|
||||
- Added a `conflict` field in `options` to declare incompatible applications
|
||||
- Removed `analytics` field in `options`
|
||||
@@ -82,7 +89,7 @@ spec:
|
||||
website: https://link.to.your.website
|
||||
sourceCode: https://link.to.sourceCode
|
||||
submitter: Submitter's Name
|
||||
language:
|
||||
locale:
|
||||
- en
|
||||
doc: https://link.to.documents
|
||||
supportArch:
|
||||
@@ -130,6 +137,13 @@ olaresManifest.version: 1.1.0
|
||||
olaresManifest.version: '2.2'
|
||||
olaresManifest.version: "3.0.122"
|
||||
```
|
||||
## apiVersion
|
||||
- Type: `string`
|
||||
- Optional
|
||||
- Accepted Value: `v1`,`v2`
|
||||
- Default: `v1`
|
||||
|
||||
For shared applications, use version `v2`, which supports multiple subcharts in a single OAC. For other applications, use `v1`.
|
||||
|
||||
## Metadata
|
||||
|
||||
@@ -152,7 +166,7 @@ metadata:
|
||||
### name
|
||||
|
||||
- Type: `string`
|
||||
- Accepted Value: `[a-z][a-z0-9]?`
|
||||
- Accepted Value: `^[a-z][a-z0-9]{0,29}$`
|
||||
|
||||
App’s namespace in Olares, lowercase alphanumeric characters only. It can be up to 30 characters, and needs to be consistent with `FolderName` and `name` field in `Chart.yaml`.
|
||||
|
||||
@@ -160,7 +174,7 @@ App’s namespace in Olares, lowercase alphanumeric characters only. It can be u
|
||||
|
||||
- Type: `string`
|
||||
|
||||
The title of your app title shown in the Olares Market. Must be within `30` characters.
|
||||
The title of your app shown in the Olares Market. Must be within `30` characters.
|
||||
|
||||
### description
|
||||
|
||||
@@ -189,8 +203,7 @@ The **Chart Version** of the application. It should be incremented each time the
|
||||
Used to display your app on different category page in Olares Market.
|
||||
|
||||
Accepted Value for OS 1.11:
|
||||
|
||||
`Blockchain`, `Utilities`, `Social Network`, `Entertainment`, `Productivity`
|
||||
- `Blockchain`, `Utilities`, `Social Network`, `Entertainment`, `Productivity`
|
||||
|
||||
Accepted Value for OS 1.12:
|
||||
- `Creativity`
|
||||
@@ -201,14 +214,13 @@ Accepted Value for OS 1.12:
|
||||
- `Utilities_v112` (displayed as Utilities)
|
||||
- `AI`
|
||||
|
||||
|
||||
:::info NOTE
|
||||
Olares Market categories were updated in OS 1.12.0. To ensure your app is compatible with both versions 1.11 and 1.12, include category values for both versions in your configuration.
|
||||
:::
|
||||
|
||||
## Entrances
|
||||
|
||||
The number of entrances through which to access the app. You must specify at least 1 access method, with a maximum of 10 allowed.
|
||||
The entrances (up to 10) that users can use to access the app. At least 1 is required.
|
||||
|
||||
:::info Example
|
||||
```yaml
|
||||
@@ -322,6 +334,24 @@ To ensure a seamless user experience, you can enable this option by setting it t
|
||||
```
|
||||
:::
|
||||
|
||||
|
||||
## sharedEntrances
|
||||
|
||||
A shared entrance is an internal address provided by a shared application for other applications within the cluster to access. The field configuration for shared entrances is basically the same as for regular entrances. A typical shared entrance configuration is shown below.
|
||||
|
||||
:::info Example
|
||||
```yaml
|
||||
sharedEntrances:
|
||||
- name: ollamav2
|
||||
host: sharedentrances-ollama
|
||||
port: 0
|
||||
title: Ollama API
|
||||
icon: https://app.cdn.olares.com/appstore/ollama/icon.png
|
||||
invisible: true
|
||||
authLevel: internal
|
||||
```
|
||||
:::
|
||||
|
||||
## Ports
|
||||
|
||||
Specify exposed ports
|
||||
@@ -338,15 +368,50 @@ ports:
|
||||
```
|
||||
:::
|
||||
|
||||
### exposePort
|
||||
- Type: `int`
|
||||
- Optional
|
||||
- Accepted Value: `0-65535`, except reserved ports `22`, `80`, `81`, `443`, `444`, `2379`, `18088`.
|
||||
|
||||
Olares will expose the ports you specify for an application, which are accessible via the application domain name in the local network, for example: `84864c1f.your_olares_id.olares.com:46879`. For each port you expose, Olares configures both TCP and UDP with the same port number.
|
||||
|
||||
When the `addToTailscaleAcl` field is set to `true`, the system will automatically assign a random port and add it to the Tailscale ACLs.
|
||||
|
||||
|
||||
:::info NOTE
|
||||
The exposed ports can only be accessed on the local network or through a VPN.
|
||||
:::
|
||||
|
||||
### protocol
|
||||
- Type: `string`
|
||||
- Optional
|
||||
- Accepted Value: `udp`, `tcp`
|
||||
|
||||
The protocol used for the exposed port. If specified, Olares exposes only the specified protocol. If omitted, Olares exposes both UDP and TCP by default.
|
||||
|
||||
### addToTailscaleAcl
|
||||
- Type: `boolean`
|
||||
- Optional
|
||||
- Default: `false`
|
||||
|
||||
When the `addToTailscaleAcl` field is set to `true`, the system will automatically assign a random port and add it to the Tailscale ACLs.
|
||||
|
||||
## Tailscale
|
||||
- Type: `map`
|
||||
- Optional
|
||||
|
||||
Allow applications to add Access Control Lists (ACL) in Tailscale to open specified ports.
|
||||
|
||||
:::info Example
|
||||
```yaml
|
||||
tailscale:
|
||||
acls:
|
||||
- proto: tcp
|
||||
dst:
|
||||
- "*:46879"
|
||||
- proto: "" # Optional. If not specified, all supported protocols will be allowed.
|
||||
dst:
|
||||
- "*:4557"
|
||||
```
|
||||
:::
|
||||
|
||||
## Permission
|
||||
|
||||
:::info Example
|
||||
@@ -380,51 +445,6 @@ Whether the app requires read and write permission to the `Data` folder. If `.Va
|
||||
|
||||
Whether the app requires read and write permission to user's `Home` folder. List all directories that the application needs to access under the user's `Home`. All `userData` directory configured in the deployment YAML, must be included here.
|
||||
|
||||
### sysData
|
||||
|
||||
- Type: `list<map>`
|
||||
- Optional
|
||||
|
||||
Declare the list of APIs that this app needs to access.
|
||||
|
||||
:::info NOTE
|
||||
This configuration has been deprecated since version 1.12.0.
|
||||
:::
|
||||
|
||||
:::info Example
|
||||
```yaml
|
||||
sysData:
|
||||
- group: service.bfl
|
||||
dataType: app
|
||||
version: v1
|
||||
ops:
|
||||
- InstallDevApp
|
||||
- dataType: legacy_prowlarr
|
||||
appName: prowlarr
|
||||
port: 9696
|
||||
group: api.prowlarr
|
||||
version: v2
|
||||
ops:
|
||||
- All
|
||||
```
|
||||
:::
|
||||
|
||||
All system API [providers](../advanced/provider.md) are list below:
|
||||
| Group | version | dataType | ops |
|
||||
| ----------- | ----------- | ----------- | ----------- |
|
||||
| service.appstore | v1 | app | InstallDevApp, UninstallDevApp
|
||||
| message-dispatcher.system-server | v1 | event | Create, List
|
||||
| service.desktop | v1 | ai_message | AIMessage
|
||||
| service.did | v1 | did | ResolveByDID, ResolveByName, Verify
|
||||
| api.intent | v1 | legacy_api | POST
|
||||
| service.intent | v1 | intent | RegisterIntentFilter, UnregisterIntentFilter, SendIntent, QueryIntent, ListDefaultChoice, CreateDefaultChoice, RemoveDefaultChoice, ReplaceDefaultChoice
|
||||
| service.message | v1 | message | GetContactLogs, GetMessages, Message
|
||||
| service.notification | v1 | message | Create
|
||||
| service.notification | v1 | token | Create
|
||||
| service.search | v1 | search | Input, Delete, InputRSS, DeleteRSS, QueryRSS, QuestionAI
|
||||
| secret.infisical | v1 | secret | CreateSecret, RetrieveSecret
|
||||
| secret.vault | v1 | key | List, Info, Sign
|
||||
|
||||
### provider
|
||||
|
||||
- Type: `list<map>`
|
||||
@@ -461,25 +481,6 @@ provider:
|
||||
```
|
||||
:::
|
||||
|
||||
## Tailscale
|
||||
- Type: `map`
|
||||
- Optional
|
||||
|
||||
Allow applications to add Access Control Lists (ACL) in Tailscale to open specified ports.
|
||||
|
||||
:::info Example
|
||||
```yaml
|
||||
tailscale:
|
||||
acls:
|
||||
- proto: tcp
|
||||
dst:
|
||||
- "*:46879"
|
||||
- proto: "" # Optional. If not specified, all supported protocols will be allowed.
|
||||
dst:
|
||||
- "*:4557"
|
||||
```
|
||||
:::
|
||||
|
||||
## Spec
|
||||
Additional information about the application, primarily used for display in the Olares Market.
|
||||
|
||||
@@ -607,7 +608,7 @@ When set to `true`, Olares forces the application to run under user ID `1000` (a
|
||||
- Type: `map`
|
||||
- Optional
|
||||
|
||||
The Olares provides highly available middleware services. Developers do not need to install middleware repeatedly. Just simply add required middleware here, You can then directly use the corresponding middleware information in the application's deployment YAML file.
|
||||
Olares provides highly available middleware services. Developers do not need to install middleware repeatedly. Add the required middleware here, then use the corresponding middleware values in the application's deployment YAML file.
|
||||
|
||||
Use the `scripts` field to specify scripts that should be executed after the database is created. Additionally, use the `extension` field to add the corresponding extension in the database.
|
||||
|
||||
@@ -803,10 +804,10 @@ Use the middleware information in deployment YAML
|
||||
|
||||
## Options
|
||||
|
||||
Configure system-related options here.
|
||||
Configure Olares OS related options here.
|
||||
|
||||
### policies
|
||||
- Type: `map`
|
||||
- Type: `list<map>`
|
||||
- Optional
|
||||
|
||||
Define detailed access control for subdomains of the app.
|
||||
@@ -823,38 +824,35 @@ options:
|
||||
```
|
||||
:::
|
||||
|
||||
### clusterScoped
|
||||
### appScope
|
||||
- Type: `map`
|
||||
- Optional
|
||||
|
||||
Whether this app is installed for all users in an Olares cluster.
|
||||
Specifies whether the app should be installed for all users in the Olares cluster. For shared apps, set `clusterScoped` to `true` and provide the current app's name in the `appRef` field.
|
||||
|
||||
:::info Example For Server
|
||||
:::info Example of ollamav2
|
||||
```yaml
|
||||
metadata:
|
||||
name: gitlab
|
||||
name: ollamav2
|
||||
options:
|
||||
appScope:
|
||||
{{- if and .Values.admin .Values.bfl.username (eq .Values.admin .Values.bfl.username) }} # Only the administrator installs the shared service
|
||||
clusterScoped: true
|
||||
appRef:
|
||||
- gitlabclienta #app name of clients
|
||||
- gitlabclientb
|
||||
```
|
||||
:::
|
||||
|
||||
:::info Example For Client
|
||||
```yaml
|
||||
metadata:
|
||||
name: gitlabclienta
|
||||
options:
|
||||
- ollamav2 # the name of current app specified in metadata.name
|
||||
{{- else }}
|
||||
clusterScoped: false
|
||||
{{- end }}
|
||||
dependencies:
|
||||
- name: olares
|
||||
version: ">=0.3.6-0"
|
||||
version: '>=1.12.3-0'
|
||||
type: system
|
||||
- name: gitlab #app name of server
|
||||
version: ">=0.0.1"
|
||||
{{- if and .Values.admin .Values.bfl.username (eq .Values.admin .Values.bfl.username) }}
|
||||
{{- else }}
|
||||
type: application
|
||||
mandatory: true
|
||||
version: '>=1.0.1'
|
||||
mandatory: true # Other users install the client, depend on the shared service installed by the admin
|
||||
{{- end }}
|
||||
```
|
||||
:::
|
||||
|
||||
@@ -880,6 +878,24 @@ options:
|
||||
```
|
||||
:::
|
||||
|
||||
### conflicts
|
||||
- Type: `list<map>`
|
||||
- Optional
|
||||
|
||||
List other applications that conflict with this app here. Conflicting apps must be uninstalled before this app can be installed.
|
||||
|
||||
:::info Example
|
||||
```yaml
|
||||
options:
|
||||
conflicts:
|
||||
- name: comfyui
|
||||
type: application
|
||||
- name: comfyuiclient
|
||||
type: application
|
||||
```
|
||||
:::
|
||||
|
||||
|
||||
### mobileSupported
|
||||
- Type: `boolean`
|
||||
- Default: `false`
|
||||
@@ -927,9 +943,8 @@ apiTimeout: 0
|
||||
:::
|
||||
|
||||
|
||||
|
||||
### allowedOutboundPorts
|
||||
- Type: `map`
|
||||
- Type: `list<int>`
|
||||
- Optional
|
||||
|
||||
The specified ports will be opened to allow external access via non-HTTP protocols, such as SMTP.
|
||||
@@ -1027,4 +1042,4 @@ provider:
|
||||
paths: ["/api*"] # API paths to expose; cannot consist of * only
|
||||
verbs: ["*"] # Supported: post, get, put, delete, patch; "*" allows all methods
|
||||
```
|
||||
:::
|
||||
:::
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
description: Reinstall Olares OS on Olares One using a bootable USB to restore the device to factory state.
|
||||
description: Reinstall Olares OS on Olares One using a bootable USB drive to restore the device to a clean initial state.
|
||||
head:
|
||||
- - meta
|
||||
- name: keywords
|
||||
content: Olares One, reinstall, factory reset, bootable USB, installation USB
|
||||
content: Olares One, reinstall, Olares OS, bootable USB, installation USB
|
||||
---
|
||||
|
||||
# Reset to factory settings using installation USB <Badge type="tip" text="15 min"/>
|
||||
# Reinstall Olares OS using bootable USB <Badge type="tip" text="15 min"/>
|
||||
|
||||
Resetting to factory settings returns your Olares One to the initial setup state. You can reinstall Olares OS using the bootable USB drive included with Olares One.
|
||||
Reinstalling Olares OS returns your Olares One to a clean initial state. You can do this using the bootable USB drive included with Olares One.
|
||||
|
||||
:::warning Data loss
|
||||
This will permanently delete all accounts, settings, and data on the device. This action cannot be undone.
|
||||
@@ -18,6 +18,9 @@ This will permanently delete all accounts, settings, and data on the device. Thi
|
||||
## Prerequisites
|
||||
**Hardware**<br>
|
||||
- The bootable USB drive that came with Olares One.
|
||||
:::tip Don't have the USB drive?
|
||||
Download the [Olares One ISO](https://cdn.olares.com/one/v1.12.4-amd64.iso), which is device-specific and different from the standard Olares ISO, and flash it to a USB drive (8 GB or larger) using a tool such as [Balena Etcher](https://etcher.balena.io/).
|
||||
:::
|
||||
- A monitor and keyboard connected to Olares One.
|
||||
|
||||
## Step 1: Boot from the USB drive
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
description: Learn how to restore your Olares One to factory settings in BIOS.
|
||||
description: Learn how to restore BIOS defaults on Olares One to return the device to its initial setup state.
|
||||
head:
|
||||
- - meta
|
||||
- name: keywords
|
||||
content: Factory reset, Olares One, BIOS
|
||||
content: Olares One, BIOS defaults, restore, BIOS setup
|
||||
---
|
||||
# Reset to factory settings in BIOS <Badge type="tip" text="10 min" />
|
||||
# Restore BIOS defaults <Badge type="tip" text="10 min" />
|
||||
|
||||
Resetting to factory settings returns your Olares One to its initial setup state. If you have a monitor and keyboard connected, you can perform this reset directly in BIOS instead of using LarePass.
|
||||
Restoring BIOS defaults resets the firmware configuration and returns your Olares One to its initial setup state. If you have a monitor and keyboard connected, you can perform this directly in BIOS.
|
||||
|
||||
:::warning Data loss
|
||||
This will permanently delete all accounts, settings, and data on the device. This action cannot be undone.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
description: Learn how to restore your Olares One to factory settings using LarePass.
|
||||
description: Learn how to factory reset your Olares One using LarePass.
|
||||
head:
|
||||
- - meta
|
||||
- name: keywords
|
||||
content: Factory reset, Olares One
|
||||
content: factory reset, Olares One, LarePass
|
||||
---
|
||||
# Reset to factory settings using LarePass <Badge type="tip" text="10 min" />
|
||||
# Factory reset via LarePass <Badge type="tip" text="10 min" />
|
||||
|
||||
If you have already activated Olares One and want to return it to the factory state, you can perform a reset in LarePass.
|
||||
|
||||
|
||||
BIN
docs/public/images/manual/use-cases/openclaw-connected1.png
Normal file
BIN
docs/public/images/manual/use-cases/openclaw-connected1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 215 KiB |
BIN
docs/public/images/manual/use-cases/openclaw-hatch-finish.png
Normal file
BIN
docs/public/images/manual/use-cases/openclaw-hatch-finish.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
BIN
docs/public/images/manual/use-cases/openclaw-persona-files.png
Normal file
BIN
docs/public/images/manual/use-cases/openclaw-persona-files.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 184 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 136 KiB |
@@ -1,6 +1,6 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
description: Learn how to install, configure, and integrate OpenClaw with Discord.
|
||||
description: Learn how to install, configure, personalize, and integrate OpenClaw with Discord.
|
||||
head:
|
||||
- - meta
|
||||
- name: keywords
|
||||
@@ -15,11 +15,10 @@ It acts as an "always-on" operator that can execute real tasks, such as searchin
|
||||
|
||||
## Learning objectives
|
||||
|
||||
By the end of this tutorial, you are be able to:
|
||||
|
||||
- Install and initialize the OpenClaw environment.
|
||||
- Pair and connect the OpenClaw CLI and the Control UI.
|
||||
- Configure OpenClaw to use the local AI model Ollama.
|
||||
- Personalize OpenClaw to establish its identity and behavior.
|
||||
- Integrate OpenClaw with Discord.
|
||||
- Enable the web search capability using Brave Search.
|
||||
- Manage skills and plug-ins.
|
||||
@@ -132,18 +131,18 @@ Run a quick setup for the agent in the OpenClaw CLI.
|
||||
- **Default Session Key**: Enter `agent:main:main`.
|
||||
7. Click **Connect**.
|
||||
|
||||
The connection error `disconnected[1008]:pairing required` occurs. This is expected and means the device connection is waiting for approval.
|
||||
The connection error `pairing required` occurs. This is expected and means the device connection is waiting for approval.
|
||||
8. Return to the OpenClaw CLI window and enter the following command:
|
||||
|
||||
```bash
|
||||
openclaw devices approve --latest
|
||||
```
|
||||
9. When the terminal displays the approval message, return to the Control UI and refresh it.
|
||||
9. When the terminal displays the approval message, return to the Control UI.
|
||||

|
||||
|
||||
Now the **STATUS** in the **Snapshot** panel should be **Connected**.
|
||||
Now the **STATUS** in the **Snapshot** panel should be **OK**.
|
||||
|
||||

|
||||

|
||||
|
||||
:::tip For advanced users
|
||||
If you prefer to fully customize your initial setup, you can run the `openclaw onboard` command instead to launch the interactive configuration wizard.
|
||||
@@ -163,7 +162,7 @@ Connect the Control UI to the OpenClaw CLI to use the graphical dashboard.
|
||||
- **Default Session Key**: Enter `agent:main:main`.
|
||||
3. Click **Connect**.
|
||||
|
||||
The connection error `disconnected[1008]:pairing required` occurs. This is expected and means the device connection is waiting for approval.
|
||||
The connection error `pairing required` occurs. This is expected and means the device connection is waiting for approval.
|
||||
4. Return to the OpenClaw CLI window and enter the following command:
|
||||
```bash
|
||||
openclaw devices list
|
||||
@@ -181,9 +180,9 @@ Connect the Control UI to the OpenClaw CLI to use the graphical dashboard.
|
||||
```bash
|
||||
openclaw devices approve {RequestID}
|
||||
```
|
||||
7. When the terminal displays the approval message, return to the Control UI. Now the **STATUS** in the **Snapshot** panel should be **Connected**.
|
||||
7. When the terminal displays the approval message, return to the Control UI. Now the **STATUS** in the **Snapshot** panel should be **OK**.
|
||||
|
||||

|
||||

|
||||
|
||||
## Configure local AI model
|
||||
|
||||
@@ -207,6 +206,75 @@ Connect the Control UI to the OpenClaw CLI to use the graphical dashboard.
|
||||
```
|
||||
4. Click **Save** in the upper-right corner. The system validates the config and restarts automatically to apply the changes.
|
||||
|
||||
::: tip Manual restart
|
||||
If you need to restart OpenClaw manually, do not use the OpenClaw CLI. Use one of the following methods:
|
||||
- **Restart the app from Settings or Market**:
|
||||
- Open **Settings**, go to **Applications** > **OpenClaw**, click **Stop**, and then click **Resume**.
|
||||
- Open **Market**, go to **My Olares**, find **OpenClaw**, click <i class="material-symbols-outlined">keyboard_arrow_down</i> next to the operation button, select **Stop**, and then select **Resume**.
|
||||
- **Restart the container**: Open **Control Hub**, click `clawdbot` under **Deployments**, and then click **Restart**.
|
||||
:::
|
||||
|
||||
## (Optional) Personalize OpenClaw
|
||||
|
||||
To make your OpenClaw bot more personalized, it is highly recommended to complete the persona setup process.
|
||||
|
||||
This process establishes the agent's identity, behavioral boundaries, and long-term memory through persona files. These files keep your agent's behavior consistent across all platforms and channels.
|
||||
|
||||
1. In the Control UI, select **Chat** from the left sidebar.
|
||||
2. Ensure <i class="material-symbols-outlined">neurology</i> at the upper-right corner is enabled. This allows you to watch the agent think and edit persona files in real time.
|
||||
3. Enter and send the following message to start:
|
||||
```text
|
||||
Wake up please!
|
||||
```
|
||||
The agent responds and starts interviewing you. You can establish rules, personality traits, and preferences. For example,
|
||||
|
||||
```text
|
||||
- Call me Bella. I like simple language without technical jargons and
|
||||
concise bulleted answers.
|
||||
- You are John, a witty assistant who uses emojis.
|
||||
- Never access my calendar without asking first, and never execute any
|
||||
financial operations.
|
||||
```
|
||||
4. As you chat with the agent, look for the **Edit** messages. These indicate the agent is successfully writing your preferences to its core persona files, such as `IDENTITY.md`, `USER.md`, and `SOUL.md`.
|
||||
|
||||
{width=90%}
|
||||
|
||||
:::tip
|
||||
If you do not see the intermediate persona file operations, refresh the page by clicking <i class="material-symbols-outlined">refresh</i> at the upper-right corner or by pressing F5.
|
||||
:::
|
||||
5. Continue the conversation until the agent gathers enough information. Then, it automatically deletes the temporary `BOOTSTRAP.md` file to finish the personalization process.
|
||||
|
||||
{width=90%}
|
||||
|
||||
6. (Optional) If the agent fails to update the persona files or delete `BOOTSTRAP.md`, explicitly instruct it to do so in the chat.
|
||||
|
||||
If the issue persists, resolve it using one of the following methods:
|
||||
- **Increase the context window**: Select **Config** from the left sidebar, switch to the **Raw** tab, find the `models` section, and then increase the `contextWindow` value to at least 64K (200K is recommended).
|
||||
|
||||
:::tip
|
||||
Note that a larger context window consumes more VRAM, so choose a value that your hardware can support.
|
||||
:::
|
||||
|
||||
- **Change the model**: Switch to a model with better tool-calling and instruction‑following capabilities.
|
||||
|
||||
7. Verify your agent's persona files are updated:
|
||||
|
||||
a. Open Files from the Launchpad.
|
||||
|
||||
b. Go to **Application** > **Data** > **clawdbot** > **config** > **workspace**.
|
||||
|
||||
c. Check the modified time of the `.md` files to identify which ones were recently updated, such as `USER.md` and `IDENTITY.md`.
|
||||
|
||||
{width=90%}
|
||||
|
||||
d. (Optional) Double-click a file to verify that it contains your newly established rules such as name, language style, and restrictions.
|
||||
|
||||
:::tip Modify persona settings
|
||||
To change these settings in the future, use one of the following methods:
|
||||
- Ask the agent in the chat to update its rules.
|
||||
- Download the `.md` files from this folder, edit them in a text editor, and re-upload them to overwrite the old ones.
|
||||
:::
|
||||
|
||||
## Integrate with Discord
|
||||
|
||||
To chat with your agent remotely, connect it to a Discord bot.
|
||||
@@ -251,14 +319,16 @@ To chat with your agent remotely, connect it to a Discord bot.
|
||||
|
||||
### Step 3: Configure channel
|
||||
|
||||
Configure the Discord channel in Control UI.
|
||||
Connect OpenClaw to your Discord bot by adding its configuration in the Control UI.
|
||||
|
||||
:::info About channel configuration
|
||||
This tutorial provides the basic setup to get your bot running in Discord quickly. For more detailed configurations, see the official [OpenClaw documentation](https://docs.openclaw.ai/channels).
|
||||
:::
|
||||
|
||||
1. Return to the **Control UI** > **Config** > **Raw** tab.
|
||||
2. Find the `channels` section:
|
||||
2. Add the following `channels` section to the configuration file.
|
||||
|
||||
a. Update with your Discord bot token.
|
||||
|
||||
b. Enable Discord DM (Direct Messages) and set the Discord DM Policy to **Pairing**.
|
||||
This configuration enables Discord Direct Messages (DMs) and sets the DM policy to pairing for security.
|
||||
|
||||
```json
|
||||
"channels": {
|
||||
@@ -276,8 +346,9 @@ Configure the Discord channel in Control UI.
|
||||
|
||||

|
||||
|
||||
3. Click **Save**.
|
||||
4. From the left sidebar, select **Channels**. On the Discord card, **Probe ok** indicates successful connection.
|
||||
3. Replace `{YOUR_BOT_TOKEN}` with your Discord bot token.
|
||||
4. Click **Save**.
|
||||
5. From the left sidebar, select **Channels**. On the Discord card, **Probe ok** indicates successful connection.
|
||||
|
||||

|
||||
|
||||
@@ -345,7 +416,7 @@ OpenClaw officially recommends Brave Search. It uses an independent web index op
|
||||
|
||||
## Manage skills and plugins
|
||||
|
||||
OpenClaw can be extended using skills and plugins:
|
||||
OpenClaw can be extended using skills and plugins:
|
||||
- Skills add new capabilities to the AI. For example, managing Model Context Protocol servers.
|
||||
- Plugins extend the system to support additional channels or community features. For example, adding iMessage via BlueBubbles.
|
||||
|
||||
@@ -442,7 +513,35 @@ To manage skills and plugins, install ClawHub. It is the package manager for Ope
|
||||
|
||||

|
||||
|
||||
6. Click **Save** in the upper-right corner. The system validates the config and restarts automatically to apply the changes.
|
||||
6. Click **Save** in the upper-right corner. The system validates the config and restarts automatically to apply the changes.
|
||||
|
||||
::: tip Manual restart
|
||||
If you need to restart OpenClaw manually, do not use the OpenClaw CLI. Use one of the following methods:
|
||||
- **Restart the app from Settings or Market**:
|
||||
- Open **Settings**, go to **Applications** > **OpenClaw**, click **Stop**, and then click **Resume**.
|
||||
- Open **Market**, go to **My Olares**, find **OpenClaw**, click <i class="material-symbols-outlined">keyboard_arrow_down</i> next to the operation button, select **Stop**, and then select **Resume**.
|
||||
- **Restart the container**: Open **Control Hub**, click `clawdbot` under **Deployments**, and then click **Restart**.
|
||||
:::
|
||||
|
||||
## FAQ
|
||||
|
||||
### Cannot restart OpenClaw in CLI
|
||||
|
||||
If you attempt to manually start, stop, or restart OpenClaw using commands like `openclaw gateway` or `openclaw gateway stop` in the OpenClaw CLI, you receive the following error messages:
|
||||
- `Gateway failed to start: gateway already running (pid 1); lock timeout after 5000ms`
|
||||
- `Gateway service check failed: Error: systemctl --user unavailable: spawn systemctl ENOENT`
|
||||
|
||||
#### Cause
|
||||
|
||||
OpenClaw is deployed as a containerized app in Olares, where the gateway runs as the primary container process `pid 1` and is always active. This environment does not use standard Linux system and service management tools such as `systemd` and `systemctl`, so these commands do not work.
|
||||
|
||||
#### Solution
|
||||
|
||||
Do not use the OpenClaw CLI to manage the gateway service. Instead, restart OpenClaw using one of the following methods:
|
||||
- **Restart OpenClaw from Settings or Market**:
|
||||
- Open **Settings**, go to **Applications** > **OpenClaw**, click **Stop**, and then click then **Resume**.
|
||||
- Open **Market**, go to **My Olares**, find **OpenClaw**, click <i class="material-symbols-outlined">keyboard_arrow_down</i> next to the operation button, select **Stop**, and then select **Resume**.
|
||||
- **Restart the container**: Open **Control Hub**, click `clawdbot` under **Deployments**, and then click **Restart**.
|
||||
|
||||
## Resources
|
||||
|
||||
|
||||
@@ -7,7 +7,15 @@ outline: [2, 3]
|
||||
每一个 Olares 应用的 Chart 根目录下都必须有一个名为 `OlaresManifest.yaml` 的文件。`OlaresManifest.yaml` 描述了一个 Olares 应用的所有基本信息。Olares 应用市场协议和 Olares 系统依赖这些关键信息来正确分发和安装应用。
|
||||
|
||||
:::info 提示
|
||||
最新的 Olares 系统使用的 Manifest 版本为: `0.10.0`
|
||||
最新的 Olares 系统使用的 Manifest 版本为: `0.11.0`
|
||||
- 移除 已不支持的sysData 配置项
|
||||
- 修改 共享应用的案例
|
||||
- 增加 apiVersion 字段说明
|
||||
- 增加 共享入口的配置说明
|
||||
|
||||
:::
|
||||
:::details Changelog
|
||||
`0.10.0`
|
||||
- 修改 `categories` 分类
|
||||
- 增加 Permission 部分中 `provider` 权限的申请
|
||||
- 增加 Provider 部分,用于让应用对集群内暴露指定服务接口
|
||||
@@ -15,8 +23,7 @@ outline: [2, 3]
|
||||
- 移除 Option 部分已不支持的一些配置项
|
||||
- 增加 `allowMultipleInstall` 配置,允许应用克隆出多个独立的实例
|
||||
- 增加 Envs 部分,支持应用声明需要的环境变量
|
||||
:::
|
||||
:::details Changelog
|
||||
|
||||
`0.9.0`
|
||||
- 在 `options` 中增加 `conflict` 字段, 用于声明不兼容的应用
|
||||
- 移除 `options` 中 `analytics` 配置项
|
||||
@@ -82,7 +89,7 @@ spec:
|
||||
website: https://link.to.your.website
|
||||
sourceCode: https://link.to.sourceCode
|
||||
submitter: Submitter's Name
|
||||
language:
|
||||
locale:
|
||||
- en
|
||||
doc: https://link.to.documents
|
||||
supportArch:
|
||||
@@ -131,6 +138,14 @@ olaresManifest.version: '2.2'
|
||||
olaresManifest.version: "3.0.122"
|
||||
```
|
||||
|
||||
## apiVersion
|
||||
- 类型:`string`
|
||||
- 可选
|
||||
- 有效值:`v1`,`v2`
|
||||
- 默认值:`v1`
|
||||
|
||||
共享应用需使用 `v2` 版本,支持一个 OAC 中包含多个子图表。其他应用请使用`v1`
|
||||
|
||||
## Metadata
|
||||
|
||||
应用的基本信息,用于在 Olares 系统和应用市场中展示应用。
|
||||
@@ -152,7 +167,7 @@ metadata:
|
||||
### name
|
||||
|
||||
- 类型:`string`
|
||||
- Accepted Value: `[a-z][a-z0-9]?`
|
||||
- 有效值:`^[a-z][a-z0-9]{0,29}$`
|
||||
|
||||
Olares 中的应用的命名空间,仅限小写字母数字字符。最多 30 个字符,需要与 `Chart.yaml` 中的 `FolderName` 和 `name` 字段保持一致。
|
||||
|
||||
@@ -200,8 +215,6 @@ OS 1.12 有效值:
|
||||
- `Utilities_v112`:实用工具
|
||||
- `AI`:AI
|
||||
|
||||
|
||||
|
||||
:::info 提示
|
||||
Olares OS 1.12.0 版本对应用商店的应用分类进行了调整,因此如果应用需要同时兼容 1.11 和 1.12 版本,请同时填写两个版本所需的分类。
|
||||
:::
|
||||
@@ -322,6 +335,23 @@ entrances:
|
||||
```
|
||||
:::
|
||||
|
||||
## sharedEntrances
|
||||
|
||||
共享入口是共享应用为集群内其他应用调用提供的接口地址。共享入口的字段配置和常规入口基本一致,一个典型的共享入口配置如下
|
||||
|
||||
:::info 示例
|
||||
```yaml
|
||||
sharedEntrances:
|
||||
- name: ollamav2
|
||||
host: sharedentrances-ollama
|
||||
port: 0
|
||||
title: Ollama API
|
||||
icon: https://app.cdn.olares.com/appstore/ollama/icon.png
|
||||
invisible: true
|
||||
authLevel: internal
|
||||
```
|
||||
:::
|
||||
|
||||
## Ports
|
||||
|
||||
定义暴露的端口
|
||||
@@ -338,14 +368,48 @@ ports:
|
||||
```
|
||||
:::
|
||||
|
||||
### exposePort
|
||||
- 类型: `int`
|
||||
- 可选
|
||||
- 有效值: `0-65535`,保留端口 `22`, `80`, `81`, `443`, `444`, `2379`, `18088` 除外
|
||||
Olares 会为你的应用暴露指定的端口,这些端口可通过应用域名在本地网络下访问,如`84864c1f.your_olares_id.olares.com:46879`。对于每个公开的端口,Olares 会自动配置相同端口号的 TCP 和 UDP。
|
||||
|
||||
当将 `addToTailscaleAcl` 字段设置为 `true` 时,系统会为该端口分配一个随机端口,并自动将其加入到 Tailscale 的 ACL 中。
|
||||
|
||||
:::info 提示
|
||||
暴露的端口只能通过本地网络或 Olares 专用网络访问。
|
||||
:::
|
||||
|
||||
### protocol
|
||||
- 类型: `string`
|
||||
- 可选
|
||||
- 有效值: `udp`、`tcp`
|
||||
|
||||
暴露端口使用的协议 ,如果不填默认同时开通udp和tcp。
|
||||
|
||||
### addToTailscaleAcl
|
||||
- 类型: `boolean`
|
||||
- 可选
|
||||
- 默认值:`false`
|
||||
|
||||
当将 addToTailscaleAcl 字段设置为 true 时,系统会为该端口分配一个随机端口,并自动将其加入到 Tailscale 的 ACL 中。
|
||||
|
||||
## Tailscale
|
||||
- 类型:`map`
|
||||
- 可选
|
||||
|
||||
允许应用在 Tailscale 的ACL(Access Control Lists)中开放指定端口。
|
||||
|
||||
:::info 示例
|
||||
```yaml
|
||||
tailscale:
|
||||
acls:
|
||||
- proto: tcp
|
||||
dst:
|
||||
- "*:46879"
|
||||
- proto: "" # 可选, 如果未指定,则允许使用所有支持的协议
|
||||
dst:
|
||||
- "*:4557"
|
||||
```
|
||||
:::
|
||||
|
||||
## Permission
|
||||
|
||||
@@ -380,51 +444,6 @@ permission:
|
||||
|
||||
应用是否需要对用户的 `Home` 文件夹进行读写权限。列出应用需要访问的用户 `Home` 下的所有目录。部署 YAML 中配置的所有 `userData` 目录都必须包含在此处。
|
||||
|
||||
### sysData
|
||||
|
||||
- 类型:`list<map>`
|
||||
- 可选
|
||||
|
||||
声明该应用程序需要访问的 API 列表。
|
||||
|
||||
:::info 提示
|
||||
从 1.12.0 版本开始,该权限配置已经被废弃。
|
||||
:::
|
||||
|
||||
:::info 示例
|
||||
```yaml
|
||||
sysData:
|
||||
- group: service.bfl
|
||||
dataType: app
|
||||
version: v1
|
||||
ops:
|
||||
- InstallDevApp
|
||||
- dataType: legacy_prowlarr
|
||||
appName: prowlarr
|
||||
port: 9696
|
||||
group: api.prowlarr
|
||||
version: v2
|
||||
ops:
|
||||
- All
|
||||
```
|
||||
:::
|
||||
|
||||
所有系统 API [providers](../advanced/provider.md) 如下:
|
||||
| Group | version | dataType | ops |
|
||||
| ----------- | ----------- | ----------- | ----------- |
|
||||
| service.appstore | v1 | app | InstallDevApp, UninstallDevApp
|
||||
| message-disptahcer.system-server | v1 | event | Create, List
|
||||
| service.desktop | v1 | ai_message | AIMessage
|
||||
| service.did | v1 | did | ResolveByDID, ResolveByName, Verify
|
||||
| api.intent | v1 | legacy_api | POST
|
||||
| service.intent | v1 | intent | RegisterIntentFilter, UnregisterIntentFilter, SendIntent, QueryIntent, ListDefaultChoice, CreateDefaultChoice, RemoveDefaultChoice, ReplaceDefaultChoice
|
||||
| service.message | v1 | message | GetContactLogs, GetMessages, Message
|
||||
| service.notification | v1 | message | Create
|
||||
| service.notification | v1 | token | Create
|
||||
| service.search | v1 | search | Input, Delete, InputRSS, DeleteRSS, QueryRSS, QuestionAI
|
||||
| secret.infisical | v1 | secret | CreateSecret, RetrieveSecret
|
||||
| secret.vault | v1 | key | List, Info, Sign
|
||||
|
||||
### provider
|
||||
|
||||
- 类型:`list<map>`
|
||||
@@ -432,7 +451,11 @@ permission:
|
||||
|
||||
用于声明本应用需访问的其他应用接口。被访问的应用需在其 `provider` 部分声明对外开放的 `providerName`,详见下方 Provider 章节。
|
||||
|
||||
此处 `appName` 应填写目标应用的 `name`,`providerName` 填写目标应用 `provider` 配置中的 `name` 字段。`podSelectors` 字段用于指定本应用中哪些 pod 需要访问目标应用。如果未声明此字段,则默认为本应用的所有 pod 注入 `outbound envoy sidecar`。
|
||||
配置访问的方式如下
|
||||
1. 在 `appName` 字段填写目标应用的 `name` 字段。
|
||||
2. 在`providerName` 字段填写目标应用 `provider` 配置中的 `name` 字段。
|
||||
|
||||
你可以使用 `podSelectors` 字段来指定本应用中哪些 pod 需要访问目标应用。如果未声明此字段,则默认为本应用的所有 pod 注入 `outbound envoy sidecar`。
|
||||
|
||||
:::info 调用应用示例
|
||||
```yaml
|
||||
@@ -458,25 +481,6 @@ provider:
|
||||
:::
|
||||
|
||||
|
||||
## Tailscale
|
||||
- 类型:`map`
|
||||
- 可选
|
||||
|
||||
允许应用在 Tailscale 的ACL(Access Control Lists)中开放指定端口。
|
||||
|
||||
:::info 示例
|
||||
```yaml
|
||||
tailscale:
|
||||
acls:
|
||||
- proto: tcp
|
||||
dst:
|
||||
- "*:46879"
|
||||
- proto: "" # 可选, 如果未指定,则允许使用所有支持的协议
|
||||
dst:
|
||||
- "*:4557"
|
||||
```
|
||||
:::
|
||||
|
||||
## Spec
|
||||
记录额外的应用信息,主要用于应用商店的展示。
|
||||
|
||||
@@ -796,10 +800,10 @@ middleware:
|
||||
|
||||
## Options
|
||||
|
||||
在此部分配置系统相关的选项。
|
||||
此部分用于配置与Olares系统相关的选项。
|
||||
|
||||
### policies
|
||||
- 类型:`map`
|
||||
- 类型:`list<map>`
|
||||
- 可选
|
||||
|
||||
定义应用子域的详细访问控制。
|
||||
@@ -816,40 +820,40 @@ options:
|
||||
```
|
||||
:::
|
||||
|
||||
### clusterScoped
|
||||
### appScope
|
||||
- 类型:`map`
|
||||
- 可选
|
||||
|
||||
是否为 Olares 集群中的所有用户安装此应用程序。
|
||||
是否为 Olares 集群中的所有用户安装此应用程序。对用共享应用,需要设置 `clusterScoped` 为 `true`, 同时在 `appRef` 字段填入应用名称
|
||||
|
||||
:::info 服务端示例
|
||||
|
||||
:::info 应用ollamav2示例
|
||||
```yaml
|
||||
metadata:
|
||||
name: gitlab
|
||||
name: ollamav2
|
||||
options:
|
||||
appScope:
|
||||
{{- if and .Values.admin .Values.bfl.username (eq .Values.admin .Values.bfl.username) }} # 仅管理员安装共享服务
|
||||
clusterScoped: true
|
||||
appRef:
|
||||
- gitlabclienta # 客户端的应用名称
|
||||
- gitlabclientb
|
||||
- ollamav2 # 此应用在 metadata.name 中声明的名字
|
||||
{{- else }}
|
||||
clusterScoped: false
|
||||
{{- end }}
|
||||
dependencies:
|
||||
- name: olares
|
||||
version: '>=1.12.3-0'
|
||||
type: system
|
||||
{{- if and .Values.admin .Values.bfl.username (eq .Values.admin .Values.bfl.username) }}
|
||||
{{- else }}
|
||||
- name: ollamav2
|
||||
type: application
|
||||
version: '>=1.0.1'
|
||||
mandatory: true # 其他用户安装客户端,依赖管理员安装的共享服务
|
||||
{{- end }}
|
||||
```
|
||||
:::
|
||||
|
||||
:::info 客户端示例
|
||||
```yaml
|
||||
metadata:
|
||||
name: gitlabclienta
|
||||
options:
|
||||
dependencies:
|
||||
- name: olares
|
||||
version: ">=0.3.6-0"
|
||||
type: system
|
||||
- name: gitlab # 服务器端的应用名称
|
||||
version: ">=0.0.1"
|
||||
type: application
|
||||
mandatory: true
|
||||
```
|
||||
:::
|
||||
|
||||
### dependencies
|
||||
- 类型:`list<map>`
|
||||
@@ -872,6 +876,24 @@ options:
|
||||
```
|
||||
:::
|
||||
|
||||
### conflicts
|
||||
- 类型:`list<map>`
|
||||
- 可选
|
||||
|
||||
请在此处声明与该应用冲突的其他应用。必须卸载冲突应用后才能安装此应用。
|
||||
|
||||
:::info 示例
|
||||
```yaml
|
||||
options:
|
||||
conflicts:
|
||||
- name: comfyui
|
||||
type: application
|
||||
- name: comfyuiclient
|
||||
type: application
|
||||
```
|
||||
:::
|
||||
|
||||
|
||||
### mobileSupported
|
||||
- 类型: `boolean`
|
||||
- 默认值: `false`
|
||||
@@ -919,7 +941,7 @@ apiTimeout: 0
|
||||
:::
|
||||
|
||||
### allowedOutboundPorts
|
||||
- 类型: `map`
|
||||
- 类型: `list<int>`
|
||||
- 可选
|
||||
|
||||
要求开通以下端口进行非 HTTP 协议的对外访问,例如 SMTP 服务等。
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
description: Reinstall Olares OS on Olares One using a bootable USB to restore the device to factory state.
|
||||
description: Reinstall Olares OS on Olares One using a bootable USB drive to restore the device to a clean initial state.
|
||||
head:
|
||||
- - meta
|
||||
- name: keywords
|
||||
content: Olares One, reinstall, factory reset, bootable USB, installation USB
|
||||
content: Olares One, reinstall, Olares OS, bootable USB, installation USB
|
||||
---
|
||||
|
||||
# Reset to factory settings using installation USB <Badge type="tip" text="15 min"/>
|
||||
# Reinstall Olares OS using bootable USB <Badge type="tip" text="15 min"/>
|
||||
|
||||
Resetting to factory settings returns your Olares One to the initial setup state. You can reinstall Olares OS using the bootable USB drive included with Olares One.
|
||||
Reinstalling Olares OS returns your Olares One to a clean initial state. You can do this using the bootable USB drive included with Olares One.
|
||||
|
||||
:::warning Data loss
|
||||
This will permanently delete all accounts, settings, and data on the device. This action cannot be undone.
|
||||
@@ -18,6 +18,9 @@ This will permanently delete all accounts, settings, and data on the device. Thi
|
||||
## Prerequisites
|
||||
**Hardware**<br>
|
||||
- The bootable USB drive that came with Olares One.
|
||||
:::tip Don't have the USB drive?
|
||||
Download the [Olares One ISO](https://cdn.olares.com/one/v1.12.4-amd64.iso), which is device-specific and different from the standard Olares ISO, and flash it to a USB drive (8 GB or larger) using a tool such as [Balena Etcher](https://etcher.balena.io/).
|
||||
:::
|
||||
- A monitor and keyboard connected to Olares One.
|
||||
|
||||
## Step 1: Boot from the USB drive
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
description: Learn how to restore your Olares One to factory settings in BIOS.
|
||||
description: Learn how to restore BIOS defaults on Olares One to return the device to its initial setup state.
|
||||
head:
|
||||
- - meta
|
||||
- name: keywords
|
||||
content: Factory reset, Olares One, BIOS
|
||||
content: Olares One, BIOS defaults, restore, BIOS setup
|
||||
---
|
||||
# Reset to factory settings in BIOS <Badge type="tip" text="10 min" />
|
||||
# Restore BIOS defaults <Badge type="tip" text="10 min" />
|
||||
|
||||
Resetting to factory settings returns your Olares One to its initial setup state. If you have a monitor and keyboard connected, you can perform this reset directly in BIOS instead of using LarePass.
|
||||
Restoring BIOS defaults resets the firmware configuration and returns your Olares One to its initial setup state. If you have a monitor and keyboard connected, you can perform this directly in BIOS.
|
||||
|
||||
:::warning Data loss
|
||||
This will permanently delete all accounts, settings, and data on the device. This action cannot be undone.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
outline: [2, 3]
|
||||
description: Learn how to restore your Olares One to factory settings using LarePass.
|
||||
description: Learn how to factory reset your Olares One using LarePass.
|
||||
head:
|
||||
- - meta
|
||||
- name: keywords
|
||||
content: Factory reset, Olares One
|
||||
content: factory reset, Olares One, LarePass
|
||||
---
|
||||
# Reset to factory settings using LarePass <Badge type="tip" text="10 min" />
|
||||
# Factory reset via LarePass <Badge type="tip" text="10 min" />
|
||||
|
||||
If you have already activated Olares One and want to return it to the factory state, you can perform a reset in LarePass.
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ metadata:
|
||||
rules:
|
||||
- nonResourceURLs:
|
||||
- "/api/reset/*"
|
||||
- "/cli/api/reset/*"
|
||||
verbs: ["*"]
|
||||
|
||||
---
|
||||
|
||||
@@ -266,7 +266,7 @@ spec:
|
||||
|
||||
containers:
|
||||
- name: api
|
||||
image: beclab/bfl:v0.4.40
|
||||
image: beclab/bfl:v0.4.42
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
runAsUser: 1000
|
||||
|
||||
@@ -178,8 +178,8 @@ func (h *Handler) setupAppCustomDomain(req *restful.Request, resp *restful.Respo
|
||||
var settings app_service.ApplicationsSettings
|
||||
appServiceClient := app_service.NewAppServiceClient()
|
||||
|
||||
var terminusName, zone string
|
||||
terminusName, zone, err = h.getUserInfo()
|
||||
var zone string
|
||||
_, zone, err = h.getUserInfo()
|
||||
if err != nil {
|
||||
response.HandleError(resp, err)
|
||||
return
|
||||
@@ -223,8 +223,6 @@ func (h *Handler) setupAppCustomDomain(req *restful.Request, resp *restful.Respo
|
||||
return
|
||||
}
|
||||
|
||||
cm := certmanager.NewCertManager(constants.TerminusName(terminusName))
|
||||
|
||||
var operate = h.getCustomDomainOperation(reqCustomDomain, existsAppCustomDomain)
|
||||
log.Infof("setAppCustomDomain: app: %s-%s, reqDomain: %s, existsDomain: %s, operate: %d, req: %s",
|
||||
appName, entranceName, reqCustomDomain, existsAppCustomDomain, operate, utils.ToJSON(customDomain))
|
||||
@@ -260,12 +258,6 @@ func (h *Handler) setupAppCustomDomain(req *restful.Request, resp *restful.Respo
|
||||
customDomain = entranceCustomDomainMap
|
||||
}
|
||||
case constants.CustomDomainDelete, constants.CustomDomainUpdate:
|
||||
_, err := cm.DeleteCustomDomainOnCloudflare(existsAppCustomDomain)
|
||||
if err != nil {
|
||||
log.Errorf("setAppCustomDomain: app: %s-%s, delete custom domain error %v", appName, entranceName, err)
|
||||
response.HandleError(resp, err)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case constants.CustomDomainAdd:
|
||||
formatSettings(customDomain, zone, "", "")
|
||||
|
||||
@@ -97,6 +97,8 @@ var (
|
||||
|
||||
APIDNSSetCloudFlareTunnel string
|
||||
|
||||
APIMyExternalIP string
|
||||
|
||||
NameSSLConfigMapName = "zone-ssl-config"
|
||||
|
||||
nameParamters = "name=%s"
|
||||
@@ -319,6 +321,11 @@ func ReloadEnvDependantVars() error {
|
||||
return err
|
||||
}
|
||||
|
||||
APIMyExternalIP, err = url.JoinPath(OlaresRemoteService, "/myip/ip")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
APIFormatCertGenerateRequest = APIPrefixCertService + "/generate?" + nameParamters
|
||||
|
||||
APIFormatCertGenerateStatus = APIPrefixCertService + "/status?" + nameParamters
|
||||
|
||||
@@ -3,14 +3,14 @@ package utils
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"bytetrade.io/web3os/bfl/internal/log"
|
||||
"bytetrade.io/web3os/bfl/pkg/constants"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -40,13 +40,6 @@ func RemoteIp(req *http.Request) string {
|
||||
|
||||
// GetMyExternalIPAddr get my network outgoing ip address
|
||||
func GetMyExternalIPAddr() string {
|
||||
sites := map[string]string{
|
||||
"httpbin": "https://httpbin.org/ip",
|
||||
"ifconfigme": "https://ifconfig.me/all.json",
|
||||
"externalip": "https://myexternalip.com/json",
|
||||
"joinolares": "https://myip.joinolares.cn/ip",
|
||||
}
|
||||
|
||||
type httpBin struct {
|
||||
Origin string `json:"origin"`
|
||||
}
|
||||
@@ -66,96 +59,74 @@ func GetMyExternalIPAddr() string {
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
var unmarshalFuncs = map[string]func(v []byte) string{
|
||||
"httpbin": func(v []byte) string {
|
||||
var hb httpBin
|
||||
if err := json.Unmarshal(v, &hb); err == nil && hb.Origin != "" {
|
||||
return hb.Origin
|
||||
}
|
||||
return ""
|
||||
},
|
||||
"ifconfigme": func(v []byte) string {
|
||||
var ifMe ifconfigMe
|
||||
if err := json.Unmarshal(v, &ifMe); err == nil && ifMe.IPAddr != "" {
|
||||
return ifMe.IPAddr
|
||||
}
|
||||
return ""
|
||||
},
|
||||
"externalip": func(v []byte) string {
|
||||
var extip externalIP
|
||||
if err := json.Unmarshal(v, &extip); err == nil && extip.IP != "" {
|
||||
return extip.IP
|
||||
}
|
||||
return ""
|
||||
},
|
||||
"joinolares": func(v []byte) string {
|
||||
return strings.TrimSpace(string(v))
|
||||
},
|
||||
type siteConfig struct {
|
||||
url string
|
||||
unmarshalFunc func(v []byte) string
|
||||
}
|
||||
|
||||
var mu sync.Mutex
|
||||
ch := make(chan any, len(sites))
|
||||
chSyncOp := func(f func()) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if ch != nil {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
for site := range sites {
|
||||
go func(name string) {
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
c := http.Client{Timeout: 5 * time.Second}
|
||||
resp, err := c.Get(sites[name])
|
||||
if err != nil {
|
||||
chSyncOp(func() { ch <- err })
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
chSyncOp(func() { ch <- err })
|
||||
return
|
||||
}
|
||||
|
||||
ip := unmarshalFuncs[name](respBytes)
|
||||
//println(name, site, ip)
|
||||
chSyncOp(func() { ch <- ip })
|
||||
|
||||
}(site)
|
||||
}
|
||||
|
||||
tr := time.NewTimer(time.Duration(5*len(sites)+3) * time.Second)
|
||||
defer func() {
|
||||
tr.Stop()
|
||||
chSyncOp(func() {
|
||||
close(ch)
|
||||
ch = nil
|
||||
})
|
||||
}()
|
||||
|
||||
LOOP:
|
||||
for i := 0; i < len(sites); i++ {
|
||||
select {
|
||||
case r, ok := <-ch:
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch v := r.(type) {
|
||||
case string:
|
||||
ip := net.ParseIP(v)
|
||||
if ip != nil && ip.To4() != nil && !ip.IsLoopback() && !ip.IsMulticast() {
|
||||
return v
|
||||
sites := []siteConfig{
|
||||
{
|
||||
url: constants.APIMyExternalIP,
|
||||
unmarshalFunc: func(v []byte) string {
|
||||
return strings.TrimSpace(string(v))
|
||||
},
|
||||
},
|
||||
{
|
||||
url: "https://httpbin.org/ip",
|
||||
unmarshalFunc: func(v []byte) string {
|
||||
var hb httpBin
|
||||
if err := json.Unmarshal(v, &hb); err == nil && hb.Origin != "" {
|
||||
return hb.Origin
|
||||
}
|
||||
case error:
|
||||
log.Warnf("got an error, %v", v)
|
||||
}
|
||||
case <-tr.C:
|
||||
tr.Stop()
|
||||
log.Warnf("timed out")
|
||||
break LOOP
|
||||
return ""
|
||||
},
|
||||
},
|
||||
{
|
||||
url: "https://ifconfig.me/all.json",
|
||||
unmarshalFunc: func(v []byte) string {
|
||||
var ifMe ifconfigMe
|
||||
if err := json.Unmarshal(v, &ifMe); err == nil && ifMe.IPAddr != "" {
|
||||
return ifMe.IPAddr
|
||||
}
|
||||
return ""
|
||||
},
|
||||
},
|
||||
{
|
||||
url: "https://myexternalip.com/json",
|
||||
unmarshalFunc: func(v []byte) string {
|
||||
var extip externalIP
|
||||
if err := json.Unmarshal(v, &extip); err == nil && extip.IP != "" {
|
||||
return extip.IP
|
||||
}
|
||||
return ""
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
client := http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
}
|
||||
for _, site := range sites {
|
||||
resp, err := client.Get(site.url)
|
||||
if err != nil {
|
||||
log.Warnf("failed to get external ip from %s, %v", site.url, err)
|
||||
continue
|
||||
}
|
||||
|
||||
respBytes, readErr := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if readErr != nil {
|
||||
log.Warnf("failed to read response from %s, %v", site.url, readErr)
|
||||
continue
|
||||
}
|
||||
|
||||
ipStr := site.unmarshalFunc(respBytes)
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip != nil && ip.To4() != nil && !ip.IsLoopback() && !ip.IsMulticast() {
|
||||
return ipStr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user