Compare commits

...

37 Commits

Author SHA1 Message Date
hys
a4f6045a90 cli: feat amdgpu install 2026-01-21 14:59:13 +08:00
eball
8d34cc995d daemon: modify mDNS registration method (#2427)
daemon: update zeroconf dependency to v0.2.5 and modify mDNS registration method
2026-01-19 23:28:29 +08:00
Yajing
a19e81a4a0 docs: refactor local access guide (#2419)
* docs: refactor local access guide

* Apply suggestions from code review

Co-authored-by: Meow33 <supermonkey03@163.com>

* address comments

---------

Co-authored-by: Meow33 <supermonkey03@163.com>
2026-01-19 21:59:08 +08:00
hysyeah
ee3f2a7df2 tapr: add max retry for delete action (#2426)
* tapr: upgrade pod template and image for PGCluster reconciliation (#2213)

* tapr: upgrade pod template and image for PGCluster reconciliation

* fix(ci): specify working directory in github action for tapr (#2215)

---------

Co-authored-by: dkeven <82354774+dkeven@users.noreply.github.com>

* tapr: upgrade pod template and image for PGCluster reconciliation

* fix(kvrocks): update init container image and pull policy configuration (#2331)

* tapr: change kvrocks running as root by default

* fix: add max retry for delete action

* tapr: update middleware-operator image tag to 0.2.31

---------

Co-authored-by: eball <liuy102@hotmail.com>
Co-authored-by: dkeven <82354774+dkeven@users.noreply.github.com>
2026-01-19 20:31:35 +08:00
eball
73ea65b004 hami: revert hami-core latest update (#2424) 2026-01-19 19:26:28 +08:00
wiy
4ef2e7124a feat(olares-app): update olares-app version to v1.7.7 (#2423) 2026-01-19 14:06:43 +08:00
wiy
ef46f91ec7 feat(olares-app): update new version to v1.7.6 (#2422)
fix(share): fixed the error message that appeared after exceeding the upload limit.
2026-01-16 23:57:38 +08:00
hysyeah
0f5a346d86 authelia: fix target url parse method (#2421) 2026-01-16 23:57:12 +08:00
salt
caa799e902 feat: optimize highlight segment order (#2420)
Co-authored-by: ubuntu <you@example.com>
2026-01-16 15:42:12 +08:00
Power-One-2025
2be5f6d108 docs: add lobechat tutorial (#2368)
* docs/feat/add-lobechat-tutorial

* docs/feat/fix-images

* docs/feat/lobechat-fixlink

* docs/feat/iterate-content

* docs/update/more-content

* docs/updaate/refine

* docs/feat/lobechat-refine

* docs/feat/add-lobechat-index

* docs/updates/fix-link

* Update docs/use-cases/lobechat.md

Co-authored-by: Meow33 <supermonkey03@163.com>

* Update docs/use-cases/lobechat.md

Co-authored-by: Meow33 <supermonkey03@163.com>

* Update docs/use-cases/lobechat.md

Co-authored-by: Meow33 <supermonkey03@163.com>

* Update docs/use-cases/lobechat.md

Co-authored-by: Meow33 <supermonkey03@163.com>

* docs/update/address-comments

* Apply suggestions from code review

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* docs/update/address-comment

* docs/update/conflict

* refine edit

* docs/updates/image-size-opt

* docs/update/resize

* Apply suggestions from code review

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* docs/update/add-faq

---------

Co-authored-by: Meow33 <supermonkey03@163.com>
Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>
2026-01-15 23:29:06 +08:00
salt
05f3c8ffdc fix: fix meaningless word highlight (#2418)
Co-authored-by: ubuntu <you@example.com>
2026-01-15 19:30:06 +08:00
berg
c0e242b05c settings: update search origin (#2417)
feat: update system frontend version
2026-01-15 19:29:07 +08:00
Power-One-2025
7929e420b1 docs: add stirling-pdf tutorial (#2369) 2026-01-15 16:10:05 +08:00
Power-One-2025
66de213f43 docs/update/image-size-opt 2026-01-15 15:56:25 +08:00
Power-One-2025
2166cec66f docs/update/fixtoc 2026-01-15 15:37:44 +08:00
Power-One-2025
1a0f9727c4 Merge branch 'main' into docs/add-stirling-pdf-tutorial 2026-01-15 11:07:46 +08:00
hysyeah
810253fe94 kubeblocks: skip check pod spec,status image (#2414)
fix: skip check pod spec,status image
2026-01-14 23:57:45 +08:00
wiy
23429a6193 olares-app, login: update version to v1.7.4 (#2413) 2026-01-14 23:57:01 +08:00
salt
49e40f316f fix: fix english highight missing (#2412)
Co-authored-by: ubuntu <you@example.com>
2026-01-14 23:56:37 +08:00
eball
1e7b655826 daemon: handle missing auth token for WebSocket connections (#2411) 2026-01-14 23:56:09 +08:00
dkeven
adea16ce7e feat(gpu): update gpu plugin version to v2.6.8 (#2410) 2026-01-14 23:55:48 +08:00
Power-One-2025
c2222859a5 docs: add PDFMathTranslate tutorial (#2378)
* docs/feat/draft

* docs/update/more-content

* docs/updates/refine

* docs/update/fix-build-conflict

* docs/update/fix-broken-link

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* docs/updates/compress-images

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* docs/update/comments

* docs/update/refine

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* docs/update/comment

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* docs/update/fix-link

* Update docs/use-cases/pdfmathtranslate.md

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>

* docs/update/comment

---------

Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>
2026-01-14 23:17:55 +08:00
yyh
e7dde2ff51 user-service: update mtranserverv2 (#2408)
fix(user-service): update mtranserverv2
2026-01-13 23:57:31 +08:00
lovehunter9
d06c1e8a99 fix: files check disk space for upload link and copy (#2407) 2026-01-13 23:57:01 +08:00
dkeven
131faacce0 feat(cli): sync kubeconfig for the original user invoking sudo (#2406) 2026-01-13 23:56:08 +08:00
Power-One-2025
09e61aecad docs/update/address-comment 2026-01-12 15:49:11 +08:00
Power-One-2025
bc5fd5fd82 Merge branch 'main' into docs/add-stirling-pdf-tutorial 2026-01-12 15:38:50 +08:00
Power-One-2025
1367355661 docs/update/address-comments 2026-01-12 15:36:38 +08:00
Power-One-2025
30195f1513 Update docs/use-cases/stirling-pdf.md
Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>
2026-01-12 14:42:28 +08:00
Power-One-2025
88b140ccc2 Update docs/use-cases/stirling-pdf.md
Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>
2026-01-12 14:42:14 +08:00
Power-One-2025
39947f464c Update docs/use-cases/stirling-pdf.md
Co-authored-by: Yajing <110797546+fnalways@users.noreply.github.com>
2026-01-12 14:41:31 +08:00
Power-One-2025
7440e85c2e docs/update/address-comments 2026-01-07 14:23:29 +08:00
Power-One-2025
d71747928c Update docs/use-cases/stirling-pdf.md
Co-authored-by: Meow33 <supermonkey03@163.com>
2026-01-07 11:31:48 +08:00
Power-One-2025
b20d5c0876 docs/feat/stirlingpdf-refine 2026-01-05 14:39:56 +08:00
Power-One-2025
c4fc3198bb docs/feat/stirlingpdf-add-index 2026-01-05 13:42:47 +08:00
Power-One-2025
260b6154f3 docs/feat/stirlingpdf-more 2026-01-05 13:38:33 +08:00
Power-One-2025
ecfcd0d1d8 docs/feat/content-add 2026-01-04 22:29:05 +08:00
112 changed files with 1899 additions and 581 deletions

View File

@@ -317,7 +317,7 @@ spec:
chown -R 1000:1000 /uploadstemp && \
chown -R 1000:1000 /appdata
- name: olares-app-init
image: beclab/system-frontend:v1.7.1
image: beclab/system-frontend:v1.7.7
imagePullPolicy: IfNotPresent
command:
- /bin/sh
@@ -439,7 +439,7 @@ spec:
- name: NATS_SUBJECT_VAULT
value: os.vault.{{ .Values.bfl.username}}
- name: user-service
image: beclab/user-service:v0.0.81
image: beclab/user-service:v0.0.82
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000

View File

@@ -0,0 +1,21 @@
package amdgpu
import (
"log"
"github.com/beclab/Olares/cli/pkg/pipelines"
"github.com/spf13/cobra"
)
func NewCmdAmdGpuInstall() *cobra.Command {
cmd := &cobra.Command{
Use: "install",
Short: "Install AMD ROCm stack via amdgpu-install",
Run: func(cmd *cobra.Command, args []string) {
if err := pipelines.AmdGpuInstall(); err != nil {
log.Fatalf("error: %v", err)
}
},
}
return cmd
}

View File

@@ -0,0 +1,16 @@
package amdgpu
import "github.com/spf13/cobra"
func NewCmdAmdGpu() *cobra.Command {
cmd := &cobra.Command{
Use: "amdgpu",
Short: "Manage AMD GPU ROCm stack",
}
cmd.AddCommand(NewCmdAmdGpuInstall())
cmd.AddCommand(NewCmdAmdGpuUninstall())
cmd.AddCommand(NewCmdAmdGpuStatus())
return cmd
}

View File

@@ -0,0 +1,21 @@
package amdgpu
import (
"log"
"github.com/beclab/Olares/cli/pkg/pipelines"
"github.com/spf13/cobra"
)
func NewCmdAmdGpuStatus() *cobra.Command {
cmd := &cobra.Command{
Use: "status",
Short: "Show AMD GPU driver and ROCm status",
Run: func(cmd *cobra.Command, args []string) {
if err := pipelines.AmdGpuStatus(); err != nil {
log.Fatalf("error: %v", err)
}
},
}
return cmd
}

View File

@@ -0,0 +1,21 @@
package amdgpu
import (
"log"
"github.com/beclab/Olares/cli/pkg/pipelines"
"github.com/spf13/cobra"
)
func NewCmdAmdGpuUninstall() *cobra.Command {
cmd := &cobra.Command{
Use: "uninstall",
Short: "Uninstall AMD ROCm stack via amdgpu-install",
Run: func(cmd *cobra.Command, args []string) {
if err := pipelines.AmdGpuUninstall(); err != nil {
log.Fatalf("error: %v", err)
}
},
}
return cmd
}

View File

@@ -1,6 +1,7 @@
package ctl
import (
"github.com/beclab/Olares/cli/cmd/ctl/amdgpu"
"github.com/beclab/Olares/cli/cmd/ctl/disk"
"github.com/beclab/Olares/cli/cmd/ctl/gpu"
"github.com/beclab/Olares/cli/cmd/ctl/node"
@@ -33,6 +34,7 @@ func NewDefaultCommand() *cobra.Command {
cmds.AddCommand(os.NewOSCommands()...)
cmds.AddCommand(node.NewNodeCommand())
cmds.AddCommand(gpu.NewCmdGpu())
cmds.AddCommand(amdgpu.NewCmdAmdGpu())
cmds.AddCommand(user.NewUserCommand())
cmds.AddCommand(disk.NewDiskCommand())

133
cli/pkg/amdgpu/tasks.go Normal file
View File

@@ -0,0 +1,133 @@
package amdgpu
import (
"fmt"
"os/exec"
"path"
"path/filepath"
"github.com/beclab/Olares/cli/pkg/common"
cc "github.com/beclab/Olares/cli/pkg/core/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"
"github.com/beclab/Olares/cli/pkg/utils"
"github.com/Masterminds/semver/v3"
"github.com/pkg/errors"
)
// InstallAmdRocmModule installs AMD ROCm stack on supported Ubuntu if AMD GPU is present.
type InstallAmdRocmModule struct {
common.KubeModule
}
func (m *InstallAmdRocmModule) Init() {
m.Name = "InstallAMDGPU"
installAmd := &task.RemoteTask{
Name: "InstallAmdRocm",
Hosts: m.Runtime.GetHostsByRole(common.Master),
Action: &InstallAmdRocm{
// no manifest needed
},
Parallel: false,
Retry: 1,
}
m.Tasks = []task.Interface{
installAmd,
}
}
// InstallAmdRocm installs ROCm using amdgpu-install on Ubuntu 22.04/24.04 for AMD GPUs.
type InstallAmdRocm struct {
common.KubeAction
}
func (t *InstallAmdRocm) Execute(runtime connector.Runtime) error {
si := runtime.GetSystemInfo()
if !si.IsLinux() || !si.IsUbuntu() || !(si.IsUbuntuVersionEqual(connector.Ubuntu2204) || si.IsUbuntuVersionEqual(connector.Ubuntu2404)) {
return nil
}
amdGPUExists, err := utils.HasAmdIGPU(runtime)
if err != nil {
return err
}
// skip rocm install
if !amdGPUExists {
return nil
}
rocmV, _ := utils.RocmVersion()
min := semver.MustParse("7.1.1")
if rocmV != nil && rocmV.LessThan(min) {
return fmt.Errorf("detected ROCm version %s, which is lower than required %s; please uninstall existing ROCm/AMDGPU components before installation with command: olares-cli amdgpu uninstall", rocmV.Original(), min.Original())
}
if rocmV != nil && rocmV.GreaterThan(min) {
logger.Warnf("Warning: detected ROCm version %s great than maximum tested version %s")
return nil
}
if rocmV != nil && rocmV.Equal(min) {
logger.Infof("detected ROCm version %s, skip rocm install...", min.Original())
return nil
}
// ensure python3-setuptools and python3-wheel
_, _ = runtime.GetRunner().SudoCmd("apt-get update", false, true)
checkPkgs := "dpkg -s python3-setuptools python3-wheel >/dev/null 2>&1 || DEBIAN_FRONTEND=noninteractive apt-get install -y python3-setuptools python3-wheel"
if _, err := runtime.GetRunner().SudoCmd(checkPkgs, false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to install python3-setuptools and python3-wheel")
}
// ensure amdgpu-install exists
if _, err := exec.LookPath("amdgpu-install"); err != nil {
var debURL string
if si.IsUbuntuVersionEqual(connector.Ubuntu2404) {
debURL = "https://repo.radeon.com/amdgpu-install/7.1.1/ubuntu/noble/amdgpu-install_7.1.1.70101-1_all.deb"
} else {
debURL = "https://repo.radeon.com/amdgpu-install/7.1.1/ubuntu/jammy/amdgpu-install_7.1.1.70101-1_all.deb"
}
tmpDeb := path.Join(runtime.GetBaseDir(), cc.PackageCacheDir, "gpu", "amdgpu-install_7.1.1.70101-1_all.deb")
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("install -d -m 0755 %s", filepath.Dir(tmpDeb)), false, true); err != nil {
return err
}
cmd := fmt.Sprintf("sh -c 'wget -O %s %s'", tmpDeb, debURL)
if _, err := runtime.GetRunner().SudoCmd(cmd, false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to download amdgpu-install deb")
}
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("DEBIAN_FRONTEND=noninteractive apt-get install -y %s", tmpDeb), false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to install amdgpu-install deb")
}
}
// run installer for rocm usecase
if _, err := runtime.GetRunner().SudoCmd("amdgpu-install -y --usecase=rocm", false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to install AMD ROCm via amdgpu-install")
}
fmt.Println()
logger.Warn("Warning: To enable ROCm, please reboot your machine after installation.")
return nil
}
type AmdgpuInstallAction struct {
common.KubeAction
}
func (t *AmdgpuInstallAction) Execute(runtime connector.Runtime) error {
if _, err := runtime.GetRunner().SudoCmd("amdgpu-install -y --usecase=rocm", false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to install AMD ROCm via amdgpu-install")
}
return nil
}
type AmdgpuUninstallAction struct {
common.KubeAction
}
func (t *AmdgpuUninstallAction) Execute(runtime connector.Runtime) error {
if _, err := runtime.GetRunner().SudoCmd("amdgpu-install --uninstall -y", false, true); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to uninstall AMD ROCm via amdgpu-install")
}
fmt.Println()
logger.Warn("Warning: Please reboot your machine after uninstall to fully remove ROCm components.")
return nil
}

View File

@@ -81,6 +81,7 @@ func (m *RunPrechecksModule) Init() {
new(NvidiaCardArchChecker),
new(NouveauChecker),
new(CudaChecker),
new(RocmChecker),
}
runPreChecks := &task.LocalTask{
Name: "RunPrechecks",

View File

@@ -372,6 +372,48 @@ func (c *CudaChecker) Check(runtime connector.Runtime) error {
return nil
}
// RocmChecker checks AMD ROCm version for AMD GPU on Ubuntu 22.04/24.04 only.
type RocmChecker struct{}
func (r *RocmChecker) Name() string {
return "ROCm"
}
func (r *RocmChecker) Check(runtime connector.Runtime) error {
if !runtime.GetSystemInfo().IsLinux() {
return nil
}
si := runtime.GetSystemInfo()
if !si.IsUbuntu() || !(si.IsUbuntuVersionEqual(connector.Ubuntu2204) || si.IsUbuntuVersionEqual(connector.Ubuntu2404)) {
return nil
}
// detect AMD GPU presence
amdGPUExists, err := utils.HasAmdIGPU(runtime)
if err != nil {
return err
}
// no AMD GPU found, no need to check rocm
if !amdGPUExists {
return nil
}
curV, err := utils.RocmVersion()
if err != nil && !os.IsNotExist(err) {
return err
}
if os.IsNotExist(err) {
return nil
}
min := semver.MustParse("7.1.1")
if curV.LessThan(min) {
return fmt.Errorf("detected ROCm version %s, which is lower than required %s; please uninstall existing ROCm/AMDGPU components before installation with command: olares-cli amdgpu uninstall", curV.Original(), min.Original())
}
return nil
}
//////////////////////////////////////////////
// precheck - task

View File

@@ -51,10 +51,12 @@ func (d DebianVersion) String() string {
}
const (
Ubuntu20 UbuntuVersion = "20."
Ubuntu22 UbuntuVersion = "22."
Ubuntu24 UbuntuVersion = "24."
Ubuntu25 UbuntuVersion = "25."
Ubuntu20 UbuntuVersion = "20."
Ubuntu22 UbuntuVersion = "22."
Ubuntu24 UbuntuVersion = "24."
Ubuntu25 UbuntuVersion = "25."
Ubuntu2204 UbuntuVersion = "22.04"
Ubuntu2404 UbuntuVersion = "24.04"
Debian9 DebianVersion = "9"
Debian10 DebianVersion = "10"

View File

@@ -36,6 +36,7 @@ import (
"github.com/beclab/Olares/cli/pkg/k3s/templates"
"github.com/beclab/Olares/cli/pkg/manifest"
"github.com/beclab/Olares/cli/pkg/registry"
"github.com/beclab/Olares/cli/pkg/storage"
)
type InstallContainerModule struct {
@@ -470,6 +471,18 @@ func (j *JoinNodesModule) Init() {
Parallel: true,
}
createSharedLibDirForWorker := &task.RemoteTask{
Name: "CreateSharedLibDir(k3s)",
Desc: "Create shared lib directory on worker",
Hosts: j.Runtime.GetHostsByRole(common.Worker),
Prepare: &prepare.PrepareCollection{
&kubernetes.NodeInCluster{Not: true},
new(common.OnlyWorker),
},
Action: new(storage.CreateSharedLibDir),
Parallel: true,
}
enableK3s := &task.RemoteTask{
Name: "EnableK3sService",
Desc: "Enable k3s service",
@@ -536,6 +549,7 @@ func (j *JoinNodesModule) Init() {
k3sService,
k3sEnv,
k3sRegistryConfig,
createSharedLibDirForWorker,
enableK3s,
copyKubeConfigForMaster,
syncKubeConfigToWorker,

View File

@@ -397,53 +397,23 @@ type CopyK3sKubeConfig struct {
}
func (c *CopyK3sKubeConfig) Execute(runtime connector.Runtime) error {
createConfigDirCmd := "mkdir -p /root/.kube && mkdir -p $HOME/.kube"
getKubeConfigCmd := "cp -f /etc/rancher/k3s/k3s.yaml /root/.kube/config"
chmodKubeConfigCmd := "chmod 0600 /root/.kube/config"
targetHome, targetUID, targetGID, err := utils.ResolveSudoUserHomeAndIDs(runtime)
if err != nil {
return err
}
cmd := strings.Join([]string{createConfigDirCmd, getKubeConfigCmd, chmodKubeConfigCmd}, " && ")
if _, err := runtime.GetRunner().SudoCmd(cmd, false, false); err != nil {
cmds := []string{
"mkdir -p /root/.kube",
"cp -f /etc/rancher/k3s/k3s.yaml /root/.kube/config",
"chmod 0600 /root/.kube/config",
fmt.Sprintf("mkdir -p %s", filepath.Join(targetHome, ".kube")),
fmt.Sprintf("cp -f /etc/rancher/k3s/k3s.yaml %s", filepath.Join(targetHome, ".kube", "config")),
fmt.Sprintf("chmod 0600 %s", filepath.Join(targetHome, ".kube", "config")),
fmt.Sprintf("chown -R %s:%s %s", targetUID, targetGID, filepath.Join(targetHome, ".kube")),
}
if _, err := runtime.GetRunner().SudoCmd(strings.Join(cmds, " && "), false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "copy k3s kube config failed")
}
userMkdir := "mkdir -p $HOME/.kube"
if _, err := runtime.GetRunner().Cmd(userMkdir, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "user mkdir $HOME/.kube failed")
}
userCopyKubeConfig := "cp -f /etc/rancher/k3s/k3s.yaml $HOME/.kube/config"
if _, err := runtime.GetRunner().SudoCmd(userCopyKubeConfig, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "user copy /etc/rancher/k3s/k3s.yaml to $HOME/.kube/config failed")
}
if _, err := runtime.GetRunner().SudoCmd("chmod 0600 $HOME/.kube/config", false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "chmod k3s $HOME/.kube/config 0600 failed")
}
// userId, err := runtime.GetRunner().Cmd("echo $(id -u)", false, false)
// if err != nil {
// return errors.Wrap(errors.WithStack(err), "get user id failed")
// }
// userGroupId, err := runtime.GetRunner().Cmd("echo $(id -g)", false, false)
// if err != nil {
// return errors.Wrap(errors.WithStack(err), "get user group id failed")
// }
userId, err := runtime.GetRunner().Cmd("echo $SUDO_UID", false, false)
if err != nil {
return errors.Wrap(errors.WithStack(err), "get user id failed")
}
userGroupId, err := runtime.GetRunner().Cmd("echo $SUDO_GID", false, false)
if err != nil {
return errors.Wrap(errors.WithStack(err), "get user group id failed")
}
chownKubeConfig := fmt.Sprintf("chown -R %s:%s $HOME/.kube", userId, userGroupId)
if _, err := runtime.GetRunner().SudoCmd(chownKubeConfig, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "chown user kube config failed")
}
return nil
}
@@ -493,59 +463,29 @@ func (s *SyncKubeConfigToWorker) Execute(runtime connector.Runtime) error {
if v, ok := s.PipelineCache.Get(common.ClusterStatus); ok {
cluster := v.(*K3sStatus)
createConfigDirCmd := "mkdir -p /root/.kube"
if _, err := runtime.GetRunner().SudoCmd(createConfigDirCmd, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "create .kube dir failed")
}
oldServer := "server: https://127.0.0.1:6443"
newServer := fmt.Sprintf("server: https://%s:%d",
s.KubeConf.Cluster.ControlPlaneEndpoint.Domain,
s.KubeConf.Cluster.ControlPlaneEndpoint.Port)
newKubeConfig := strings.Replace(cluster.KubeConfig, oldServer, newServer, -1)
syncKubeConfigForRootCmd := fmt.Sprintf("echo '%s' > %s", newKubeConfig, "/root/.kube/config")
if _, err := runtime.GetRunner().SudoCmd(syncKubeConfigForRootCmd, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "sync kube config for root failed")
}
if _, err := runtime.GetRunner().SudoCmd("chmod 0600 /root/.kube/config", false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "chmod k3s $HOME/.kube/config failed")
}
userConfigDirCmd := "mkdir -p $HOME/.kube"
if _, err := runtime.GetRunner().Cmd(userConfigDirCmd, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "user mkdir $HOME/.kube failed")
}
syncKubeConfigForUserCmd := fmt.Sprintf("echo '%s' > %s", newKubeConfig, "$HOME/.kube/config")
if _, err := runtime.GetRunner().Cmd(syncKubeConfigForUserCmd, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "sync kube config for normal user failed")
}
// userId, err := runtime.GetRunner().Cmd("echo $(id -u)", false, false)
// if err != nil {
// return errors.Wrap(errors.WithStack(err), "get user id failed")
// }
// userGroupId, err := runtime.GetRunner().Cmd("echo $(id -g)", false, false)
// if err != nil {
// return errors.Wrap(errors.WithStack(err), "get user group id failed")
// }
userId, err := runtime.GetRunner().Cmd("echo $SUDO_UID", false, false)
targetHome, targetUID, targetGID, err := utils.ResolveSudoUserHomeAndIDs(runtime)
if err != nil {
return errors.Wrap(errors.WithStack(err), "get user id failed")
return err
}
targetKubeConfigPath := filepath.Join(targetHome, ".kube", "config")
userGroupId, err := runtime.GetRunner().Cmd("echo $SUDO_GID", false, false)
if err != nil {
return errors.Wrap(errors.WithStack(err), "get user group id failed")
cmds := []string{
"mkdir -p /root/.kube",
fmt.Sprintf("echo '%s' > %s", newKubeConfig, "/root/.kube/config"),
"chmod 0600 /root/.kube/config",
fmt.Sprintf("mkdir -p %s", filepath.Join(targetHome, ".kube")),
fmt.Sprintf("echo '%s' > %s", newKubeConfig, targetKubeConfigPath),
fmt.Sprintf("chmod 0600 %s", targetKubeConfigPath),
fmt.Sprintf("chown -R %s:%s %s", targetUID, targetGID, filepath.Join(targetHome, ".kube")),
}
chownKubeConfig := fmt.Sprintf("chown -R %s:%s -R $HOME/.kube", userId, userGroupId)
if _, err := runtime.GetRunner().SudoCmd(chownKubeConfig, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "chown user kube config failed")
if _, err := runtime.GetRunner().SudoCmd(strings.Join(cmds, " && "), false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "sync kube config failed")
}
}
return nil

View File

@@ -23,6 +23,7 @@ import (
"github.com/beclab/Olares/cli/pkg/core/prepare"
"github.com/beclab/Olares/cli/pkg/core/task"
"github.com/beclab/Olares/cli/pkg/manifest"
"github.com/beclab/Olares/cli/pkg/storage"
)
type StatusModule struct {
@@ -243,6 +244,18 @@ func (j *JoinNodesModule) Init() {
Retry: 5,
}
createSharedLibDirForWorker := &task.RemoteTask{
Name: "CreateSharedLibDir(k8s)",
Desc: "Create shared lib directory on worker",
Hosts: j.Runtime.GetHostsByRole(common.Worker),
Prepare: &prepare.PrepareCollection{
&NodeInCluster{Not: true},
new(common.OnlyWorker),
},
Action: new(storage.CreateSharedLibDir),
Parallel: true,
}
joinWorkerNode := &task.RemoteTask{
Name: "JoinWorkerNode(k8s)",
Desc: "Join worker node",
@@ -323,6 +336,7 @@ func (j *JoinNodesModule) Init() {
j.Tasks = []task.Interface{
generateKubeadmConfig,
joinMasterNode,
createSharedLibDirForWorker,
joinWorkerNode,
copyKubeConfig,
removeMasterTaint,

View File

@@ -417,51 +417,23 @@ type CopyKubeConfigForControlPlane struct {
}
func (c *CopyKubeConfigForControlPlane) Execute(runtime connector.Runtime) error {
createConfigDirCmd := "mkdir -p /root/.kube"
getKubeConfigCmd := "cp -f /etc/kubernetes/admin.conf /root/.kube/config"
cmd := strings.Join([]string{createConfigDirCmd, getKubeConfigCmd}, " && ")
if _, err := runtime.GetRunner().SudoCmd(cmd, false, false); err != nil {
targetHome, targetUID, targetGID, err := utils.ResolveSudoUserHomeAndIDs(runtime)
if err != nil {
return err
}
cmds := []string{
"mkdir -p /root/.kube",
"cp -f /etc/kubernetes/admin.conf /root/.kube/config",
"chmod 0600 /root/.kube/config",
fmt.Sprintf("mkdir -p %s", filepath.Join(targetHome, ".kube")),
fmt.Sprintf("cp -f /etc/kubernetes/admin.conf %s", filepath.Join(targetHome, ".kube", "config")),
fmt.Sprintf("chmod 0600 %s", filepath.Join(targetHome, ".kube", "config")),
fmt.Sprintf("chown -R %s:%s %s", targetUID, targetGID, filepath.Join(targetHome, ".kube")),
}
if _, err := runtime.GetRunner().SudoCmd(strings.Join(cmds, " && "), false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "copy kube config failed")
}
userMkdir := "mkdir -p $HOME/.kube"
if _, err := runtime.GetRunner().Cmd(userMkdir, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "user mkdir $HOME/.kube failed")
}
userCopyKubeConfig := "cp -f /etc/kubernetes/admin.conf $HOME/.kube/config"
if _, err := runtime.GetRunner().SudoCmd(userCopyKubeConfig, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "user copy /etc/kubernetes/admin.conf to $HOME/.kube/config failed")
}
if _, err := runtime.GetRunner().SudoCmd("chmod 0600 $HOME/.kube/config", false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "chmod $HOME/.kube/config failed")
}
// userId, err := runtime.GetRunner().Cmd("echo $(id -u)", false, false)
// if err != nil {
// return errors.Wrap(errors.WithStack(err), "get user id failed")
// }
// userGroupId, err := runtime.GetRunner().Cmd("echo $(id -g)", false, false)
// if err != nil {
// return errors.Wrap(errors.WithStack(err), "get user group id failed")
// }
userId, err := runtime.GetRunner().Cmd("echo $SUDO_UID", false, false)
if err != nil {
return errors.Wrap(errors.WithStack(err), "get user id failed")
}
userGroupId, err := runtime.GetRunner().Cmd("echo $SUDO_GID", false, false)
if err != nil {
return errors.Wrap(errors.WithStack(err), "get user group id failed")
}
chownKubeConfig := fmt.Sprintf("chown -R %s:%s $HOME/.kube", userId, userGroupId)
if _, err := runtime.GetRunner().SudoCmd(chownKubeConfig, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "chown user kube config failed")
}
return nil
}
@@ -521,53 +493,23 @@ func (s *SyncKubeConfigToWorker) Execute(runtime connector.Runtime) error {
if v, ok := s.PipelineCache.Get(common.ClusterStatus); ok {
cluster := v.(*KubernetesStatus)
createConfigDirCmd := "mkdir -p /root/.kube"
if _, err := runtime.GetRunner().SudoCmd(createConfigDirCmd, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "create .kube dir failed")
}
syncKubeConfigForRootCmd := fmt.Sprintf("echo '%s' > %s", cluster.KubeConfig, "/root/.kube/config")
if _, err := runtime.GetRunner().SudoCmd(syncKubeConfigForRootCmd, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "sync kube config for root failed")
}
if _, err := runtime.GetRunner().SudoCmd("chmod 0600 /root/.kube/config", false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "chmod $HOME/.kube/config failed")
}
userConfigDirCmd := "mkdir -p $HOME/.kube"
if _, err := runtime.GetRunner().Cmd(userConfigDirCmd, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "user mkdir $HOME/.kube failed")
}
syncKubeConfigForUserCmd := fmt.Sprintf("echo '%s' > %s", cluster.KubeConfig, "$HOME/.kube/config")
if _, err := runtime.GetRunner().Cmd(syncKubeConfigForUserCmd, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "sync kube config for normal user failed")
}
// userId, err := runtime.GetRunner().Cmd("echo $(id -u)", false, false)
// if err != nil {
// return errors.Wrap(errors.WithStack(err), "get user id failed")
// }
// userGroupId, err := runtime.GetRunner().Cmd("echo $(id -g)", false, false)
// if err != nil {
// return errors.Wrap(errors.WithStack(err), "get user group id failed")
// }
userId, err := runtime.GetRunner().Cmd("echo $SUDO_UID", false, false)
targetHome, targetUID, targetGID, err := utils.ResolveSudoUserHomeAndIDs(runtime)
if err != nil {
return errors.Wrap(errors.WithStack(err), "get user id failed")
return err
}
targetKubeConfigPath := filepath.Join(targetHome, ".kube", "config")
userGroupId, err := runtime.GetRunner().Cmd("echo $SUDO_GID", false, false)
if err != nil {
return errors.Wrap(errors.WithStack(err), "get user group id failed")
cmds := []string{
"mkdir -p /root/.kube",
fmt.Sprintf("echo '%s' > %s", cluster.KubeConfig, "/root/.kube/config"),
"chmod 0600 /root/.kube/config",
fmt.Sprintf("mkdir -p %s", filepath.Join(targetHome, ".kube")),
fmt.Sprintf("echo '%s' > %s", cluster.KubeConfig, targetKubeConfigPath),
fmt.Sprintf("chmod 0600 %s", targetKubeConfigPath),
fmt.Sprintf("chown -R %s:%s %s", targetUID, targetGID, filepath.Join(targetHome, ".kube")),
}
chownKubeConfig := fmt.Sprintf("chown -R %s:%s -R $HOME/.kube", userId, userGroupId)
if _, err := runtime.GetRunner().SudoCmd(chownKubeConfig, false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "chown user kube config failed")
if _, err := runtime.GetRunner().SudoCmd(strings.Join(cmds, " && "), false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "sync kube config failed")
}
}
return nil

View File

@@ -3,8 +3,7 @@ package system
import (
"strings"
"github.com/beclab/Olares/cli/pkg/gpu"
"github.com/beclab/Olares/cli/pkg/amdgpu"
"github.com/beclab/Olares/cli/pkg/bootstrap/os"
"github.com/beclab/Olares/cli/pkg/bootstrap/patch"
"github.com/beclab/Olares/cli/pkg/bootstrap/precheck"
@@ -12,6 +11,7 @@ import (
"github.com/beclab/Olares/cli/pkg/container"
"github.com/beclab/Olares/cli/pkg/core/module"
"github.com/beclab/Olares/cli/pkg/daemon"
"github.com/beclab/Olares/cli/pkg/gpu"
"github.com/beclab/Olares/cli/pkg/images"
"github.com/beclab/Olares/cli/pkg/k3s"
"github.com/beclab/Olares/cli/pkg/manifest"
@@ -82,6 +82,7 @@ func (l *linuxPhaseBuilder) build() []module.Module {
addModule(&terminus.WriteReleaseFileModule{}).
addModule(gpuModuleBuilder(func() []module.Module {
return []module.Module{
&amdgpu.InstallAmdRocmModule{},
&gpu.InstallDriversModule{
ManifestModule: manifest.ManifestModule{
Manifest: l.manifestMap,

101
cli/pkg/pipelines/amdgpu.go Normal file
View File

@@ -0,0 +1,101 @@
package pipelines
import (
"strings"
"github.com/beclab/Olares/cli/pkg/amdgpu"
"github.com/beclab/Olares/cli/pkg/common"
"github.com/beclab/Olares/cli/pkg/core/action"
"github.com/beclab/Olares/cli/pkg/core/connector"
"github.com/beclab/Olares/cli/pkg/core/logger"
"github.com/beclab/Olares/cli/pkg/core/module"
"github.com/beclab/Olares/cli/pkg/core/pipeline"
"github.com/beclab/Olares/cli/pkg/core/task"
)
type singleTaskModule struct {
common.KubeModule
name string
act action.Action
}
func (m *singleTaskModule) Init() {
m.Name = m.name
m.Tasks = []task.Interface{
&task.LocalTask{
Name: m.name,
Action: m.act,
},
}
}
func AmdGpuInstall() error {
arg := common.NewArgument()
arg.SetConsoleLog("amdgpuinstall.log", true)
runtime, err := common.NewKubeRuntime(common.AllInOne, *arg)
if err != nil {
return err
}
p := &pipeline.Pipeline{
Name: "InstallAMDGPUDrivers",
Runtime: runtime,
Modules: []module.Module{
&amdgpu.InstallAmdRocmModule{},
},
}
return p.Start()
}
func AmdGpuUninstall() error {
arg := common.NewArgument()
arg.SetConsoleLog("amdgpuuninstall.log", true)
runtime, err := common.NewKubeRuntime(common.AllInOne, *arg)
if err != nil {
return err
}
p := &pipeline.Pipeline{
Name: "UninstallAMDGPUDrivers",
Runtime: runtime,
Modules: []module.Module{
&singleTaskModule{name: "AmdgpuUninstall", act: new(amdgpu.AmdgpuUninstallAction)},
},
}
return p.Start()
}
func AmdGpuStatus() error {
arg := common.NewArgument()
runtime, err := common.NewKubeRuntime(common.AllInOne, *arg)
if err != nil {
return err
}
runtime.SetRunner(
&connector.Runner{
Host: &connector.BaseHost{
Name: common.LocalHost,
Arch: runtime.GetSystemInfo().GetOsArch(),
Os: runtime.GetSystemInfo().GetOsType(),
},
},
)
amdModel, _ := runtime.GetRunner().SudoCmd("lspci | grep -iE 'VGA|3D|Display' | grep -iE 'AMD|ATI' | head -1 || true", false, false)
drvVer, _ := runtime.GetRunner().SudoCmd("modinfo amdgpu 2>/dev/null | awk -F': ' '/^version:/{print $2}' || true", false, false)
rocmVer, _ := runtime.GetRunner().SudoCmd("cat /opt/rocm/.info/version 2>/dev/null || true", false, false)
if strings.TrimSpace(amdModel) != "" {
logger.Infof("AMD GPU: %s", strings.TrimSpace(amdModel))
} else {
logger.Info("AMD GPU: not detected")
}
if strings.TrimSpace(drvVer) != "" {
logger.Infof("AMDGPU driver %s", strings.TrimSpace(drvVer))
} else {
logger.Info("AMDGPU driver version: unknown")
}
if strings.TrimSpace(rocmVer) != "" {
logger.Infof("ROCm version: %s", strings.TrimSpace(rocmVer))
} else {
logger.Info("ROCm version: not installed")
}
return nil
}

View File

@@ -396,3 +396,17 @@ func (t *DeleteTerminusData) Execute(runtime connector.Runtime) error {
return nil
}
type CreateSharedLibDir struct {
common.KubeAction
}
func (t *CreateSharedLibDir) Execute(runtime connector.Runtime) error {
if runtime.GetSystemInfo().IsDarwin() {
return nil
}
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("mkdir -p %s && chown 1000:1000 %s", OlaresSharedLibDir, OlaresSharedLibDir), false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to create shared lib dir")
}
return nil
}

View File

@@ -38,12 +38,6 @@ type InstallOsSystem struct {
}
func (t *InstallOsSystem) Execute(runtime connector.Runtime) error {
if !runtime.GetSystemInfo().IsDarwin() {
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("mkdir -p %s && chown 1000:1000 %s", storage.OlaresSharedLibDir, storage.OlaresSharedLibDir), false, false); err != nil {
return errors.Wrap(errors.WithStack(err), "failed to create shared lib dir")
}
}
config, err := ctrl.GetConfig()
if err != nil {
return err
@@ -367,6 +361,11 @@ func (m *InstallOsSystemModule) Init() {
Action: &CreateUserEnvConfigMap{},
}
createSharedLibDir := &task.LocalTask{
Name: "CreateSharedLibDir",
Action: &storage.CreateSharedLibDir{},
}
installOsSystem := &task.LocalTask{
Name: "InstallOsSystem",
Action: &InstallOsSystem{},
@@ -399,6 +398,7 @@ func (m *InstallOsSystemModule) Init() {
m.Tasks = []task.Interface{
applySystemEnv,
createUserEnvConfigMap,
createSharedLibDir,
installOsSystem,
createBackupConfigMap,
checkSystemService,

View File

@@ -9,6 +9,7 @@ import (
"github.com/beclab/Olares/cli/pkg/core/connector"
"github.com/beclab/Olares/cli/pkg/core/logger"
"github.com/beclab/Olares/cli/pkg/core/task"
"github.com/beclab/Olares/cli/pkg/utils"
)
type WelcomeMessage struct {
@@ -68,6 +69,15 @@ func (t *WelcomeMessage) Execute(runtime connector.Runtime) error {
logger.Infof("Username: %s", t.KubeConf.Arg.User.UserName)
logger.Infof("Password: %s", t.KubeConf.Arg.User.Password)
fmt.Printf("\n------------------------------------------------\n\n\n\n\n")
fmt.Println()
// If AMD GPU on Ubuntu 22.04/24.04, print warning about reboot for ROCm
if si := runtime.GetSystemInfo(); si.IsUbuntu() && (si.IsUbuntuVersionEqual(connector.Ubuntu2204) || si.IsUbuntuVersionEqual(connector.Ubuntu2404)) {
if hasAmd, _ := utils.HasAmdIGPU(runtime); hasAmd {
logger.Warnf("\x1b[31mWarning: To enable ROCm, please reboot your machine after activation.\x1b[0m")
fmt.Println()
}
}
return nil
}

67
cli/pkg/utils/amdgpu.go Normal file
View File

@@ -0,0 +1,67 @@
package utils
import (
"fmt"
"os"
"strings"
"github.com/Masterminds/semver/v3"
"github.com/beclab/Olares/cli/pkg/core/connector"
)
func HasAmdIGPU(execRuntime connector.Runtime) (bool, error) {
// Detect by CPU model names that bundle AMD AI NPU/graphics
targets := []string{
"AMD Ryzen AI Max+ 395",
"AMD Ryzen AI Max 390",
"AMD Ryzen AI Max 385",
"AMD Ryzen AI 9 HX 375",
"AMD Ryzen AI 9 HX 370",
"AMD Ryzen AI 9 365",
}
// try lscpu first: extract 'Model name' field
out, err := execRuntime.GetRunner().SudoCmd("lscpu 2>/dev/null | awk -F': *' '/^Model name/{print $2; exit}' || true", false, false)
if err != nil {
return false, err
}
if out != "" {
lo := strings.ToLower(strings.TrimSpace(out))
for _, t := range targets {
if strings.Contains(lo, strings.ToLower(t)) {
return true, nil
}
}
}
// fallback to /proc/cpuinfo
out, err = execRuntime.GetRunner().SudoCmd("awk -F': *' '/^model name/{print $2; exit}' /proc/cpuinfo 2>/dev/null || true", false, false)
if err != nil {
return false, err
}
if out != "" {
lo := strings.ToLower(strings.TrimSpace(out))
for _, t := range targets {
if strings.Contains(lo, strings.ToLower(t)) {
return true, nil
}
}
}
return false, nil
}
func RocmVersion() (*semver.Version, error) {
const rocmVersionFile = "/opt/rocm/.info/version"
data, err := os.ReadFile(rocmVersionFile)
if err != nil {
// no ROCm installed, nothing to check
if os.IsNotExist(err) {
return nil, err
}
return nil, err
}
curStr := strings.TrimSpace(string(data))
cur, err := semver.NewVersion(curStr)
if err != nil {
return nil, fmt.Errorf("invalid rocm version: %s", curStr)
}
return cur, nil
}

View File

@@ -321,3 +321,54 @@ func GetBufIOReaderOfTerminalInput() (*bufio.Reader, error) {
}
return bufio.NewReader(tty), nil
}
// ResolveSudoUserHomeAndIDs resolves the home directory, uid, and gid for the user
// who invoked sudo. If not running under sudo, it falls back to the current user.
// This is useful for commands that need to operate on the invoking user's home
// directory rather than /root when running with sudo.
func ResolveSudoUserHomeAndIDs(runtime connector.Runtime) (home, uid, gid string, err error) {
uid, err = runtime.GetRunner().Cmd("echo ${SUDO_UID:-}", false, false)
if err != nil {
return "", "", "", errors.Wrap(errors.WithStack(err), "get SUDO_UID failed")
}
gid, err = runtime.GetRunner().Cmd("echo ${SUDO_GID:-}", false, false)
if err != nil {
return "", "", "", errors.Wrap(errors.WithStack(err), "get SUDO_GID failed")
}
uid = strings.TrimSpace(uid)
gid = strings.TrimSpace(gid)
if uid == "" {
uid, err = runtime.GetRunner().Cmd("id -u", false, false)
if err != nil {
return "", "", "", errors.Wrap(errors.WithStack(err), "get current uid failed")
}
gid, err = runtime.GetRunner().Cmd("id -g", false, false)
if err != nil {
return "", "", "", errors.Wrap(errors.WithStack(err), "get current gid failed")
}
uid = strings.TrimSpace(uid)
gid = strings.TrimSpace(gid)
}
home, err = runtime.GetRunner().Cmd(fmt.Sprintf(`getent passwd %s | awk -F: 'NR==1{print $6; exit}'`, uid), false, false)
if err != nil {
home = ""
}
home = strings.TrimSpace(home)
if home == "" {
home, _ = runtime.GetRunner().Cmd(fmt.Sprintf(`awk -F: -v uid=%s '$3==uid {print $6; exit}' /etc/passwd 2>/dev/null`, uid), false, false)
home = strings.TrimSpace(home)
}
if home == "" {
home, err = runtime.GetRunner().Cmd("echo $HOME", false, false)
if err != nil {
return "", "", "", errors.Wrap(errors.WithStack(err), "get HOME failed")
}
home = strings.TrimSpace(home)
}
if home == "" {
return "", "", "", errors.New("resolve user home failed")
}
return home, uid, gid, nil
}

View File

@@ -23,7 +23,7 @@ require (
github.com/containerd/containerd v1.7.29
github.com/distribution/distribution/v3 v3.0.0
github.com/dustin/go-humanize v1.0.1
github.com/eball/zeroconf v0.2.4
github.com/eball/zeroconf v0.2.5
github.com/godbus/dbus/v5 v5.1.0
github.com/gofiber/fiber/v2 v2.52.9
github.com/google/gopacket v1.1.19

View File

@@ -87,8 +87,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eball/echo/v4 v4.13.4-patch h1:5w83KQrEqrxhc1BO0BpRBHssC37vFrWualUM27Rt2sg=
github.com/eball/echo/v4 v4.13.4-patch/go.mod h1:ORgy8LWTq8knpwgaz538rAJMri7WgpoAD6H3zYccn84=
github.com/eball/zeroconf v0.2.4 h1:S5nUHLu2zhpA8YuR/Ue/vXPiY6ynPECkpDXjYV+Ckj4=
github.com/eball/zeroconf v0.2.4/go.mod h1:eIbIjGYo9sSMaKWLcveHEPRWdyblz7q9ih2R1HnNw5M=
github.com/eball/zeroconf v0.2.5 h1:RNINVvj8kbm/r4YoqYu/jWD57l5NJmvRUCfbjlIsbJg=
github.com/eball/zeroconf v0.2.5/go.mod h1:eIbIjGYo9sSMaKWLcveHEPRWdyblz7q9ih2R1HnNw5M=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=

View File

@@ -84,8 +84,15 @@ func (p *proxyServer) Start() error {
clientIp = h
}
}
if c.IsWebSocket() {
ctx = context.WithValue(ctx, WSKey, true)
swp := c.Request().Header.Get("Sec-WebSocket-Protocol")
authToken := c.Request().Header.Get("X-Authorization")
if len(authToken) == 0 && len(swp) > 0 {
// handle missing auth token for websocket
c.Request().Header.Set("X-Authorization", swp)
}
}
r := c.Request().WithContext(ctx)
if clientIp != "" {
@@ -243,7 +250,7 @@ func (p *proxyServer) customDialContext(d *net.Dialer) func(ctx context.Context,
}
if isWs {
klog.Info("WebSocket connection detected, using upgraded dialer")
klog.Info("WebSocket connection detected, using upgraded dialer, ", addr)
return tlsDial(ctx, d, func(ctx context.Context, network, addr string) (net.Conn, error) {
return proxyDial(ctx, d, network, newAddr)
}, network, addr, &tls.Config{InsecureSkipVerify: true})

View File

@@ -99,7 +99,7 @@ func (s *server) Restart() error {
instanceName = hostname
}
s.server, err = zeroconf.Register(instanceName, s.serviceName, "local.", hostname, s.port, []string{""}, []net.Interface{*iface})
s.server, err = zeroconf.RegisterAll(instanceName, s.serviceName, "local.", hostname, s.port, []string{""}, []net.Interface{*iface}, false, false, false)
if err != nil {
klog.Error("create mdns server error, ", err)
return err

View File

@@ -124,7 +124,7 @@ const side = {
link: "/manual/larepass/back-up-mnemonics"
},
{
text: "Access Olares locally",
text: "Access Olares securely",
link: "/manual/get-started/local-access",
},
{
@@ -403,7 +403,7 @@ const side = {
],
},
{
text: "Best practices",
text: "Tutorials",
link: "/manual/best-practices/",
collapsed: true,
items: [
@@ -427,6 +427,10 @@ const side = {
text: "Expand storage in Olares",
link: "/manual/best-practices/expand-storage-in-olares",
},
{
text: "Access Olares locally",
link: "/manual/best-practices/local-access",
},
],
},
{ text: "Glossary", link: "/manual/glossary" },
@@ -554,6 +558,18 @@ const side = {
text: "ACE-Step",
link: "/use-cases/ace-step",
},
{
text: "Stirling PDF",
link: "/use-cases/stirling-pdf",
},
{
text: "PDFMathTranslate",
link: "/use-cases/pdfmathtranslate",
},
{
text: "LobeChat",
link: "/use-cases/lobechat",
},
],
},
],
@@ -904,4 +920,4 @@ export const en = defineConfig({
sidebar: side,
},
});
});

View File

@@ -0,0 +1,9 @@
<script setup lang="ts">
const targetUrl = 'https://www.olares.cn/larepass'
</script>
<template>
<a :href="targetUrl" target="_blank" rel="noreferrer" class="app-link">
<slot>LarePass 下载页面</slot>
</a>
</template>

View File

@@ -0,0 +1,9 @@
<script setup lang="ts">
const targetUrl = 'https://www.olares.com/larepass'
</script>
<template>
<a :href="targetUrl" target="_blank" rel="noreferrer" class="app-link">
<slot>LarePass Download page</slot>
</a>
</template>

View File

@@ -14,6 +14,9 @@ import OSTabs from "./components/OStabs.vue";
import VersionSwitcher from "./components/VersionSwitcher.vue";
import _ from "lodash";
import { redirects } from './redirects';
import AppLinkGlobal from './components/AppLinkGlobal.vue'
import AppLinkCN from './components/AppLinkCN.vue'
const LANGUAGE_LOCAL_KEY = "language";
let isMenuChange = false;
@@ -27,6 +30,8 @@ enhanceApp({ app, router }: { app: App; router: Router }) {
app.component("FilterableList", FilterableList);
app.component("OSTabs", OSTabs);
app.component("VersionSwitcher", VersionSwitcher);
app.component('AppLinkGlobal', AppLinkGlobal)
app.component('AppLinkCN', AppLinkCN)
router.onBeforeRouteChange = (to: string) => {
const path = to.replace(/\.html$/i, ''),
@@ -142,4 +147,4 @@ enhanceApp({ app, router }: { app: App; router: Router }) {
}
);
},
};
};

View File

@@ -123,7 +123,7 @@ const side = {
link: "/zh/manual/larepass/back-up-mnemonics",
},
{
text: "内网访问 Olares",
text: "安全访问 Olares",
link: "zh/manual/get-started/local-access",
},
{
@@ -399,7 +399,7 @@ const side = {
]
},
{
text: "Olares 进阶",
text: "教程",
collapsed: true,
link: "/zh/manual/best-practices/",
items: [
@@ -427,6 +427,10 @@ const side = {
text: "在 Olares 中扩展存储空间",
link: "/zh/manual/best-practices/expand-storage-in-olares",
},
{
text: "本地访问 Olares",
link: "/manual/best-practices/local-access",
},
],
},
{ text: "术语", link: "/zh/manual/glossary" },
@@ -905,4 +909,4 @@ export const zh = defineConfig({
sidebar: side,
},
});
});

View File

@@ -1,12 +1,13 @@
---
description: In-depth guides and proven best practices to help you get the most out of Olares.
---
# Beyond the basics: Olares best practices and advanced guides
# Tutorials
This section offers in-depth guides and proven best practices to help you get the most out of Olares. Whether you're optimizing performance, securing your environment, or customizing advanced features, these resources go beyond initial setup to support real-world scenarios and power-user workflows.
This section offers in-depth guides and proven best practices to help you get the most out of Olares.
- [Configure a custom domain for your Olares](set-custom-domain.md)
- [Install a multi-node Olares cluster](install-olares-multi-node.md)
- [Build your knowledge hub with Wise](organize-content.md)
- [Install Olares on PVE via ISO with GPU Passthrough](install-olares-gpu-passthrough.md)
- [Expand Olares storage capacity](expand-storage-in-olares.md)
- [Access Olares services locally](local-access.md)

View File

@@ -0,0 +1,250 @@
---
outline: [2,3]
description: Learn the different methods to access Olares services locally for improved speed and offline capability.
---
# Access Olares services locally
Olares is designed to provide seamless access to your self-hosted services anytime, anywhere.
However, accessing your devices locally provides several advantages:
- **Maximum performance**: Transfer files at full speed without the latency and potential bottlenecks of the internet.
- **Enhanced privacy**: Keep your traffic contained within your home network for added security.
- **Offline independence**: Access your data and apps even when your internet service is unavailable.
## Objectives
By the end of this tutorial, you will learn how to:
- Establish a secure, high-speed local connection using the LarePass VPN.
- Access Olares services using `.local` domains.
- Configure local DNS to allow standard URLs to resolve locally across your entire network.
- Manually map hosts files to ensure access on specific machines without internet.
## Choose a connection method
There are four ways to establish a local connection:
* **[Method 1: Enable LarePass VPN](#method-1-enable-larepass-vpn)**<br/>
Uses LarePass VPN to automatically detect your local network and optimize the connection speed without changing settings.
* **[Method 2: Use `.local` domain](#method-2-use-local-domain)**<br/>
Access the device via a specific local URL format. No installation required.
* **[Method 3: Configure local DNS](#method-3-configure-local-dns)**<br/>
Updates your router or computer's DNS settings to map the standard Olares URL to the local IP address.
* **[Method 4: Modify hosts files](#method-4-modify-hosts-files)**<br/>
Manually maps the standard Olares URL to the local IP on a single computer.
## Method 1: Enable LarePass VPN
The LarePass VPN is designed to secure your connection while optimizing performance. When enabled, LarePass detects if you are on the same network as your device and switches to **Intranet** mode.
:::tip Always enable VPN for remote access
Keep LarePass VPN enabled. It automatically prioritizes the fastest available route to ensure you always get the best speed possible without manual switching.
:::
:::info iOS and macOS setup
On iOS or macOS, you may be prompted to add a VPN Configuration to your system settings the first time you enable the feature. Allow this to complete the setup.
:::
Enable the LarePass VPN directly on the device you are currently using to access Olares.
<tabs>
<template #On-LarePass-mobile-client>
1. Open the LarePass app, and go to **Settings**.
2. In the **My Olares** card, toggle on the VPN switch.
![Enable LarePass VPN on mobile](/images/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #On-LarePass-desktop-client>
1. Open the LarePass app, and click your avatar in the top-left corner to open the user menu.
2. Toggle on the switch for **VPN connection**.
![Enable LarePass VPN on desktop](/images/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
Once enabled, check the status indicator in LarePass to verify the connection type:
| Status | Description |
|:-------------|:---------------------------------------------------------|
| **Intranet** | Direct connection via your local LAN IP. Fastest speeds. |
| **P2P** | Direct encrypted tunnel between devices. High speed. |
| **DERP** | Routed via a secure relay server. Used as a fallback. |
## Method 2: Use `.local` domain
If you prefer not to install additional apps, you can access services using the `.local` domain. There are two domain formats available depending on your operating system.
:::info Use HTTP protocol
The `.local` domain does not support HTTPS. You must explicitly use `http://` at the beginning of the URL.
:::
### Single-level domain (All operating systems)
:::warning Supported for community apps only
Olares system apps such as Desktop and Files do not support this URL format and will not load correctly.
:::
This format uses a single-level domain by connecting the entrance ID and the username with hyphens (`-`).
- **Default URL**:
```plain
https://<entrance_id>.<username>.olares.com
```
- **Local-access URL**:
```plain
http://<entrance_id>-<username>-olares.local
```
### Multi-level domain (macOS and iOS only)
Apple devices support local service discovery via [Bonjour](https://developer.apple.com/bonjour/) (zeroconfiguration networking), which can resolve multilabel domains under `.local` on macOS and iOS. This allows a local URL format that mirrors the remote address.
- **Default URL**:
```plain
https://<entrance_id>.<username>.olares.com
```
- **Local-access URL**:
```plain
http://<entrance_id>.<username>.olares.local
```
![Multi-level local domain](/images/manual/get-started/multilevel-local-domain-mac.png#bordered)
## Method 3: Configure local DNS
For a seamless experience where standard URLs resolve to your local IP address automatically, you can configure your network DNS. This configuration ensures consistent access across all devices on the network without requiring individual client setup.
### Find the internal IP for Olares device
To configure DNS, first you need to find the internal IP for your Olares device.
<tabs>
<template #Check-via-the-LarePass-mobile-client>
If your phone and Olares device are on the same network:
1. Open the LarePass app, and go to **Settings** > **System** to navigate to the **Olares management** page
![Tap the System card](/images/manual/get-started/larepass-system.png#bordered)
2. Tap on the device card.
![Tap the device card](/images/manual/get-started/larepass-device-card.png#bordered)
3. Scroll down to the **Network** section. You can find the **Intranet IP** there.
![Find Network section](/images/manual/get-started/larepass-network.png#bordered)
</template>
<template #Check-via-Olares-Terminal>
Control Hub provides a built-in terminal that allows you to run system commands directly from the browser, without needing an external SSH client.
1. Open the Control Hub app, and under **Terminal**, select **Olares** in the left navigation bar.
![Find internal IP from Control Hub](/images/manual/get-started/find-internal-ip-from-controlhub.png#bordered)
2. Type `ifconfig` in the terminal and press **Enter**.
3. Look for your active connection, typically named `enp3s0` (wired) or `wlo1` (wireless). The IP address follows `inet`.
Example output:
```bash
enp3s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.50.116 netmask 255.255.255.0 broadcast 192.168.50.255
inet6 fe80::4194:4045:c35e:7b32 prefixlen 64 scopeid 0x20<link>
ether d8:43:ae:54:ce:fc txqueuelen 1000 (Ethernet)
RX packets 80655321 bytes 71481515308 (71.4 GB)
RX errors 0 dropped 136 overruns 0 frame 0
TX packets 51867817 bytes 15924740708 (15.9 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
```
In this example, `192.168.50.116` is the internal IP.
</template>
</tabs>
### Configure DNS
With the internal IP address identified, you must now configure your DNS settings to route traffic correctly. You can apply this configuration to a single computer for individual access, or update your router to enable seamless local resolution for all devices on your network.
<tabs>
<template #Configure-for-local-device>
Update the DNS settings on your specific computer. For example, on macOS:
1. Open Apple menu and go to **System Settings**.
2. Select **Wi-Fi**, then click **Details** on your connected network.
3. Select **DNS** and update the server list:
a. Click the **+** button under **DNS Servers** to add your Olares device's internal IP (e.g., `192.168.x.x`).
b. Ensure the Olares IP is listed at the top. Add your original DNS (or `1.1.1.1`) below it as a fallback. <br/>This ensures that if your Olares device shuts down, the router will automatically switch to the secondary DNS, keeping your internet connection alive.
4. Click **OK** to save changes.
</template>
<template #Configure-for-all-devices>
Update the DNS on your router to apply changes to every device in your network.
1. Log in to your router's admin panel.
2. Navigate to **DHCP / DNS Settings**.
3. Set **Primary DNS** to your Olares device's internal IP (e.g., `192.168.x.x`).
4. Set **Secondary DNS** to your current Primary DNS (or a public provider like `1.1.1.1`). <br/>This ensures that if your Olares device shuts down, the router will automatically switch to the secondary DNS, keeping your internet connection alive.
5. Save and reconnect your devices to refresh the DNS cache.
</template>
</tabs>
Once configured, you can access Olares using both your standard public address and your local address.
:::tip
You can install AdGuard Home from the Olares Market to monitor traffic and manage DNS mappings graphically.
:::
## Method 4: Modify hosts files
If you cannot change router settings and need immediate offline access on a specific computer, you can manually map the domains in your hosts file.
1. Locate your hosts file:
- **Windows**: `C:\Windows\System32\drivers\etc\hosts`
- **macOS/Linux**: `/etc/hosts`
2. Open the file with a text editor, which requires Administrator privileges.
3. Add the mapping lines:
```plain
# Replace with the actual internal IP and the username
# Olares apps
192.168.31.208 desktop.<username>.olares.com
192.168.31.208 auth.<username>.olares.com
192.168.31.208 files.<username>.olares.com
192.168.31.208 market.<username>.olares.com
192.168.31.208 settings.<username>.olares.com
192.168.31.208 dashboard.<username>.olares.com
192.168.31.208 control-hub.<username>.olares.com
192.168.31.208 profile.<username>.olares.com
192.168.31.208 vault.<username>.olares.com
# Add other community apps as needed
192.168.31.208 <entrance_id>.<username>.olares.com
```
4. Save the file to apply changes and ensure local access without an internet connection.
Verify the changes by checking the URL for quick loading or using the terminal:
```bash
ping desktop.<username>.olares.com
```
If the IP address starts with `192.168`, it indicates successful configuration.
## FAQs
### Why doesn't LarePass VPN work on my Mac anymore?
If you successfully enabled the VPN previously, but it has stopped working, you might need to reset the system extension.
:::info
Depending on your macOS version, the UI might look slightly different.
:::
1. Open **System Settings**, search for "Extension", and select **Login Items & Extensions**.
2. Scroll to the **Network Extensions** section and click the info icon (ⓘ) to view loaded extensions.
3. Find LarePass, click the three dots (...), and select **Delete Extension**.
4. Confirm the uninstallation.
5. Restart your Mac and re-enable the VPN in the LarePass desktop client.
### Why can't I enable LarePass VPN on Windows?
Third-party antivirus software might mistakenly flag the LarePass desktop client as suspicious, preventing it from launching the VPN service.
If prompted by your antivirus when opening LarePass for the first time, allow the application to continue.
If the VPN still fails to enable:
1. Open your security software and check if LarePass was blocked.
2. Add the main LarePass executable to the allowlist or exclusions of your antivirus.
3. Restart LarePass and enable the VPN.
### Why the `.local` domain does not work in Chrome (macOS)?
Chrome may fail to access local URLs if macOS blocks local network permissions.
To enable access:
1. Open Apple menu and go to **System Settings**.
2. Go to **Privacy & Security** > **Local Network**.
3. Find Google Chrome and Google Chrome Helper in the list and enable the toggles.
![Enable local network](/images/manual/larepass/mac-chrome-local-access.png#bordered){width=400}
4. Restart Chrome and try accessing the local URL again.
### Why does the application fail to load in an iFrame when using a `.local` domain on Chrome (macOS)?
Chrome might default to HTTPS when using local domains, and you might see a "connection not secure" warning.
![Incorrect local address](/images/manual/get-started/incorrect-local-address.png#bordered)
To address this, explicitly add the HTTP protocol (`http://`) to the beginning of the URL. This tells Chrome it's a local, non-encrypted connection, which is expected on your home network.

View File

@@ -1,24 +1,25 @@
---
outline: [2,3]
description: Learn how to access Olares apps and services directly via your local network (LAN) for maximum speed, privacy, and offline reliability.
description: Learn how to access your Olares services securely using the LarePass VPN.
---
# Access Olares services locally
# Access Olares services securely using LarePass VPN
Typically, you access Olares services through a browser using a URL like `https://desktop.<username>.olares.com`. This way, you can reach your services from any device at any time.
However, accessing your devices directly over your Local Area Network (LAN) provides several advantages:
- **Maximum performance**: Transfer files at full speed without the latency and potential bottlenecks of the internet.
- **Enhanced privacy**: Keep your traffic contained within your home network for added security.
- **Offline independence**: Access your data and apps even when your internet service is unavailable.
While this address works from anywhere, it's recommended to enable the LarePass VPN to ensure your connection is always secure and efficient. The client automatically detects your network environment and selects the best connection method:
- **At home**: It establishes a direct **Intranet** connection to allow faster file transfers on your local network.
- **From remote**: It switches to a secure encrypted tunnel to ensure you remain connected safely when accessing remotely.
This guide covers several methods to establish a local connection:
- [Enable LarePass VPN (Recommended)](#method-1-enable-larepass-vpn)<br/>This method is the easiest solution, as it automatically establishes the fastest connection without manual configuration.
- [Use `.local` domain](#method-2-use-local-domain)<br/>This method requires no installation, though you must use specific URL formats based on your operating system.
- [Configure local DNS (Advanced)](#method-3-configure-local-dns)<br/>This method allows standard URLs to work locally by updating DNS settings on your router or individual computer.
- [Modify host files (Fallback)](#method-4-modify-host-files)<br/>This method manually maps standard URLs to your local IP on a single computer, ensuring access even without an internet connection.
## Download LarePass
To use the secure VPN connection, the LarePass client must be installed on the device you are using.
- **Mobile**: Use the LarePass app installed during the Olares ID creation process.
- **Desktop**: Download and install the LarePass desktop client.
## Method 1: Enable LarePass VPN
The most robust way to connect, whether you are sitting next to the device or traveling, is using the LarePass VPN. It intelligently detects when you are on the same network and switches to a direct **Intranet** mode for maximum speed.
1. Visit <AppLinkGlobal />.
2. Download the version compatible with your operating system.
## Enable LarePass VPN
Once installed, enable the VPN directly on the device you are using to access Olares.
:::tip Always enable VPN for remote access
Keep LarePass VPN enabled. It automatically prioritizes the fastest available route to ensure you always get the best speed possible without manual switching.
@@ -27,25 +28,24 @@ Keep LarePass VPN enabled. It automatically prioritizes the fastest available ro
On iOS or macOS, you may be prompted to add a VPN Configuration to your system settings the first time you enable the feature. Allow this to complete the setup.
:::
Enable the LarePass VPN directly on the device you are currently using to access Olares.
<tabs>
<template #On-LarePass-mobile-client>
1. Open the LarePass app, and go to **Settings**.
1. Open the LarePass app and go to **Settings**.
2. In the **My Olares** card, toggle on the VPN switch.
![Enable LarePass VPN on mobile](/images/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #On-LarePass-desktop-client>
1. Open the LarePass app, and click your avatar in the top-left corner to open the user menu.
1. Open the LarePass app and click your avatar in the top-left corner to open the user menu.
2. Toggle on the switch for **VPN connection**.
![Enable LarePass VPN on desktop](/images/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
## Verify the connection type
Once enabled, check the status indicator in LarePass to verify the connection type:
| Status | Description |
@@ -54,148 +54,6 @@ Once enabled, check the status indicator in LarePass to verify the connection ty
| **P2P** | Direct encrypted tunnel between devices. High speed. |
| **DERP** | Routed via a secure relay server. Used as a fallback. |
## Method 2: Use `.local` domain
If you prefer not to use a VPN, you can access services using the `.local` domain. There are two domain formats available depending on your compatibility needs.
### Single-level domain (All operating systems)
:::warning Supported for community apps only
Olares system apps such as Desktop and Files do not support this URL format and will not load correctly.
:::
This format uses a single-level domain by connecting the entrance ID and the username with hyphens (`-`).
- **Default URL**:
```plain
https://<entrance_id>.<username>.olares.com
```
- **Local-access URL**:
```plain
http://<entrance_id>-<username>-olares.local
```
### Multi-level domain (macOS and iOS only)
Apple devices support local service discovery via [Bonjour](https://developer.apple.com/bonjour/) (zeroconfiguration networking), which can resolve multilabel domains under `.local` on macOS and iOS. This allows a local URL format that mirrors the remote address.
- **Default URL**:
```plain
https://<entrance_id>.<username>.olares.com
```
- **Local-access URL**:
```plain
http://<entrance_id>.<username>.olares.local
```
![Multi-level local domain](/images/manual/get-started/multilevel-local-domain-mac.png#bordered)
## Method 3: Configure local DNS
For a seamless experience where standard URLs resolve to your local IP address automatically, you can configure your network DNS. This configuration ensures consistent access across all devices on the network without requiring individual client setup.
### Find the internal IP for Olares device
To configure DNS, first you need to find the internal IP for your Olares device.
<tabs>
<template #Check-via-the-LarePass-mobile-client>
If your phone and Olares device are on the same network:
1. Open the LarePass app, and go to **Settings** > **System** to navigate to the **Olares management** page
![Tap the System card](/images/manual/get-started/larepass-system.png#bordered)
2. Tap on the device card.
![Tap the device card](/images/manual/get-started/larepass-device-card.png#bordered)
3. Scroll down to the **Network** section. You can find the **Intranet IP** there.
![Find Network section](/images/manual/get-started/larepass-network.png#bordered)
</template>
<template #Check-via-Olares-Terminal>
Control Hub provides a built-in terminal that allows you to run system commands directly from the browser, without needing an external SSH client.
1. Open the Control Hub app, and under **Terminal**, select **Olares**in the left navigation bar.
![Find internal IP from Control Hub](/images/manual/get-started/find-internal-ip-from-controlhub.png#bordered)
2. Type `ifconfig` in the terminal and press **Enter**.
3. Look for your active connection, typically named `enp3s0` (wired) or `wlo1` (wireless). The IP address follows `inet`.
Example output:
```bash
enp3s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.50.116 netmask 255.255.255.0 broadcast 192.168.50.255
inet6 fe80::4194:4045:c35e:7b32 prefixlen 64 scopeid 0x20<link>
ether d8:43:ae:54:ce:fc txqueuelen 1000 (Ethernet)
RX packets 80655321 bytes 71481515308 (71.4 GB)
RX errors 0 dropped 136 overruns 0 frame 0
TX packets 51867817 bytes 15924740708 (15.9 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
```
In this example, `192.168.50.116` is the internal IP.
</template>
</tabs>
### Configure DNS
With the internal IP address identified, you must now configure your DNS settings to route traffic correctly. You can apply this configuration to a single computer for individual access, or update your router to enable seamless local resolution for all devices on your network.
<tabs>
<template #Configure-for-local-device>
Update the DNS settings on your specific computer. For example, on macOS:
1. Open Apple menu and go to **System Settings**.
2. Select **Wi-Fi**, then click **Details** on your connected network.
3. Select **DNS** and update the server list:
a. Click the **+** button under **DNS Servers** to add your Olares device's internal IP (e.g., `192.168.x.x`).
b. Ensure the Olares IP is listed at the top. Add your original DNS (or `1.1.1.1`) below it as a fallback. <br/>This ensures that if your Olares device shuts down, the router will automatically switch to the secondary DNS, keeping your internet connection alive.
4. Click **OK** to save changes.
</template>
<template #Configure-for-all-devices>
Update the DNS on your router to apply changes to every device in your network.
1. Log in to your router's admin panel.
2. Navigate to **DHCP / DNS Settings**.
3. Set **Primary DNS** to your Olares device's internal IP (e.g., `192.168.x.x`).
4. Set **Secondary DNS** to your current Primary DNS (or a public provider like `1.1.1.1`). <br/>This ensures that if your Olares device shuts down, the router will automatically switch to the secondary DNS, keeping your internet connection alive.
5. Save and reconnect your devices to refresh the DNS cache.
</template>
</tabs>
Once configured, you can access Olares using both your standard public address and your local address.
:::tip
You can install AdGuard Home from the Olares Market to monitor traffic and manage DNS mappings graphically.
:::
## Method 4: Modify host files
If you cannot change router settings and need immediate offline access on a specific computer, you can manually map the domains in your hosts file.
1. Locate your hosts file:
- **Windows:** `C:\Windows\System32\drivers\etc\hosts`
- **macOS/Linux:** `/etc/hosts`
2. Open the file with a text editor, which requires Administrator privileges.
3. Add the mapping lines:
```plain
# Replace with the actual internal IP and the username
# Olares apps
192.168.31.208 desktop.<username>.olares.com
192.168.31.208 auth.<username>.olares.com
192.168.31.208 files.<username>.olares.com
192.168.31.208 market.<username>.olares.com
192.168.31.208 settings.<username>.olares.com
192.168.31.208 dashboard.<username>.olares.com
192.168.31.208 control-hub.<username>.olares.com
192.168.31.208 profile.<username>.olares.com
192.168.31.208 vault.<username>.olares.com
# Add other community apps as needed
192.168.31.208 <entrance_id>.<username>.olares.com
```
4. Save the file to apply changes and ensure local access without an internet connection.
Verify the changes by checking the URL for quick loading or using the terminal:
```bash
ping desktop.<username>.olares.com
```
If the IP address starts with `192.168`, it indicates successful configuration.
## Learn more
- [Access Olares services remotely via LarePass VPN](../../manual/larepass/private-network.md): Understand how to use LarePass VPN.
- [Network](../../developer/concepts/network.md): Learn about the different entry points in Olares.
## FAQs
### Why doesn't LarePass VPN work on my Mac anymore?
If you successfully enabled the VPN previously, but it has stopped working, you might need to reset the system extension.
@@ -208,28 +66,16 @@ Depending on your macOS version, the UI might look slightly different.
4. Confirm the uninstallation.
5. Restart your Mac and re-enable the VPN in the LarePass desktop client.
### Why I cannot enable LarePass VPN on Windows?
### Why can't I enable LarePass VPN on Windows?
Third-party antivirus software might mistakenly flag the LarePass desktop client as suspicious, preventing it from launching the VPN service.
If prompted by your antivirus when opening LarePass for the first time, allow the application to continue.
If the VPN still fails to enable:
1. Open your security software and check if LarePass was blocked.
2. Add the main LarePass executable to the allowlist** or exclusions of your antivirus.
2. Add the main LarePass executable to the allowlist or exclusions of your antivirus.
3. Restart LarePass and enable the VPN.
### Why the `.local` domain does not work in Chrome (macOS)?
Chrome may fail to access local URLs if macOS blocks local network permissions.
To enable access:
1. Open Apple menu and go to **System Settings**.
2. Go to **Privacy & Security** > **Local Network**.
3. Find Google Chrome and Google Chrome Helper in the list and enable the toggles.
![Enable local network](/images/manual/larepass/mac-chrome-local-access.png#bordered){width=400}
4. Restart Chrome and try accessing the local URL again.
### Why does the application fail to load in an iFrame when using a `.local` domain on Chrome (macOS)?
Chrome might default to HTTPS when using local domains, and you might see a "connection not secure" warning.
![Incorrect local address](/images/manual/get-started/incorrect-local-address.png#bordered)
To address this, explicitly add the HTTP protocol (`http://`) to the beginning of the URL. This tells Chrome it's a local, non-encrypted connection, which is expected on your home network.
## Learn more
- [Access Olares locally](../best-practices/local-access.md): Explore detailed instructions for all available local network connection methods.
- [Network](../../developer/concepts/network.md): Learn about the different entry points in Olares.

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

View File

@@ -22,5 +22,8 @@ From running AI models to building seamless workflows across your self-hosted se
{ title: 'DeerFlow', link: './host-cloud-android.html', tags: ['AI']},
{ title: 'ACE-Step', link: './ace-step.html', tags: ['AI']},
{ title: 'Duix.Avatar', link: './duix-avatar.html', tags: ['AI']},
{ title: 'LobeChat', link: './lobechat.html', tags: ['AI']},
{ title: 'Stirling PDF', link: './stirling-pdf.html', tags: ['Productivity']},
{ title: 'PDFMathTranslate', link: './pdfmathtranslate.html', tags: ['AI']},
]"
/>

272
docs/use-cases/lobechat.md Normal file
View File

@@ -0,0 +1,272 @@
---
outline: [2, 4]
description: Learn how to install LobeChat on Olares and integrate it with Ollama to build and enhance your local custom AI assistants.
---
# Build your local AI assistant with LobeChat
LobeChat is an opensource framework for building secure, local AI chat experiences. It supports file handling, knowledge bases, and multimodal inputs, and it supports Ollama to run and switch local LLMs.
Olares streamlines and simplifies the deployment of both, allowing you to skip complex manual environment configurations.
This guide covers the installation, configuration, and practical usage of these tools to create your personalized AI assistants.
## Learning objectives
By the end of this guide, you are able to:
- Configure LobeChat to communicate with your local Ollama instance.
- Use LobeChat for specific scenarios such as content writing and coding.
## Prerequisites
Before you begin, make sure:
- Ollama is installed and running in your Olares environment.
- At least one model is installed using Ollama. For more information, see [Ollama](ollama.md).
## Install LobeChat
1. From the Olares Market, search for "LobeChat".
![Search for LobeChat from Market](/images/manual/use-cases/find-lobechat.png#bordered)
2. Click **Get**, and then click **Install**. Wait for the installation to finish.
## Configure the connection
After the installation is completed, you must connect LobeChat to Ollama to make the chat interface work.
1. Open **LobeChat** from Launchpad.
2. Click the LobeChat icon in the upper-left corner, and then go to **Settings** > **Language Model**.
![LobeChat settings menu](/images/manual/use-cases/lobechat-settings-menu.png#bordered){width=75%}
3. (Optional) To obtain your local Ollama host address, go to Olares **Settings** > **Application** > **Ollama** > **Ollama API** > **Set up endpoint**, and then copy the endpoint address.
![Obtain Ollama host address from Olares Settings](/images/manual/use-cases/obtain-ollama-hosturl.png#bordered){width=60%}
4. In the **Ollama** section, find the **Interface proxy address** field, and then enter your local Ollama address.
![Interface proxy address connection](/images/manual/use-cases/lobechat-connection-setting.png#bordered)
5. Click **Check** under the Ollama model list to verify the connection. A **Check Passed** message indicates that the proxy address is correct.
![Ollama connection successful](/images/manual/use-cases/ollama-model-checkpass.png#bordered)
## Install language models
By default, LobeChat lists supported models, including models that are not installed yet.
You can click **Get Model List** to fetch available models from Ollama.
To download additional models, use one of the following options:
- Download via LobeChat UI
When you are chatting with a selected language model, if the language model is not installed, you are prompted right in the chat to download and install it.
![Install language model via LobeChat UI](/images/manual/use-cases/download-in-lobechat.png#bordered)
- Download using the Ollama CLI. For more information, see [Download a model](ollama.md#download-a-model).
## Create an assistant
LobeChat allows you to create specialized assistants to handle specific tasks by leveraging various language models and combining them with functional plug-ins.
- **Flexible model switching**: You can switch language models instantly within the same chat to achieve the best results. For example, if you are not satisfied with a response, you can select a different model from the list to leverage their unique strengths.
- **Plug-in extensions**: You can also install plug-ins to extend and enhance the capabilities of your assistant.
:::info
To install plug-ins, ensure that you select a model compatible with Function Calling. Look for <i class="material-symbols-outlined">brick</i> next to the model name, which indicates the model supports function calls.
:::
The following steps outline the standard workflow for creating and configuring any assistant in LobeChat. You can apply this procedure using specific settings provided in the [use scenarios](#use-scenarios) section.
1. Create a new assistant:
- From the left navigation pane, click **New Assistant**.
- If you already have active chats, click <i class="material-symbols-outlined">add_comment</i> to create a new one.
2. Configure the assistant such as identity and role:
a. Click **Open Chat Settings**.
![Open Chat Settings](/images/manual/use-cases/open-chat-settings.png#bordered)
b. On the **Assistant Info** tab, set the avatar, name, and description, and then click **Update Assistant Information**.
![LobeChat session settings](/images/manual/use-cases/lobechat-session-settings.png#bordered)
c. On the **Role Configuration** tab, enter your prompt for this specific role to define its behavior, and then click **OK**.
d. Close the **Session Settings** page to return to the chat window.
3. Select the language model from the basic interaction area.
![Select language model](/images/manual/use-cases/select-qwen.png#bordered)
4. (Optional) Install LobeChat plug-ins to enhance the assistant's capabilities:
a. In the basic interaction area, hover over the plug-in icon and click **Plugin Store**.
![Install LobeChat plug-in](/images/manual/use-cases/lobechat-plugin-install.png#bordered)
b. On the **LobeHub Plugins** tab, search for the target plug-in, and then click **Install**.
5. Interact with the assistant.
6. (Optional) Pin for quick access:
If you are satisfied with the assistant's performance, hover over the assistant in the sidebar, click <i class="material-symbols-outlined">more_vert</i>, and then click **Pin** to keep it accessible at the top of your list.
## Use scenarios
The following scenarios provide some practical examples for your daily tasks. Apply these specific settings during the [general creation procedure](#create-an-assistant) to build specialized assistants tailored to your workflow.
### Polish content and visualize ideas
Create a specialized assistant to help you refine text and generate images based on descriptions.
#### Configurations
- **Name**: `Writing Bot`
- **Role prompt**:
```
You are a creative editor. When I provide text, review it for clarity
and tone. When I describe a scene, use the drawing plug-in to generate
an image based on my description.
```
- **Language model**: `qwen2.5:7b`
:::info
`qwen2.5:7b` excels at various NLP tasks such as contextual understanding and content writing. It is also compatible with functional calling, so you can install LobeChat plug-ins for enhanced capabilities.
:::
- **LobeChat plug-in**: "Pollinate drawing", which is used to create images based on description
![Install LobeChat plug-in Pollinate Drawing](/images/manual/use-cases/install-pollinate-drawing.png#bordered)
#### Interaction
1. Enter and send your draft content to get a refined version.
2. Hover over the plug-in icon to ensure that **Pollinate drawing** is enabled, and then ask the assistant to create a cover image for the content.
3. Brainstorm and iterate with the language model to get your ideal content textually and visually.
### Coding assistant
Create a specialized assistant to help you write efficient code and act as a dedicated pair programmer.
#### Configurations
- **Name**: `Dev Bot`
- **Role prompt**:
```
You are an expert developer. When I describe a task or requirement,
generate clean, efficient, and well-commented code to solve it.
```
- **Language model**: `deepseek-coder-v2`
:::info
`deepseek-coder-v2` is good at coding use cases such as code generation and long text understanding.
:::
#### Interaction
1. Describe a data generation task and send to the chat.
```
Write a Python script to generate a CSV file named employees.csv with
20 rows of mock data. Columns should include: ID, Name, Department,
and Salary. Use the random library to generate varied data.
```
2. The assistant processes your request and generates a standalone Python script with explanation.
```python
import csv
import random
def generate_mock_data():
departments = ['HR', 'Engineering', 'Marketing', 'Sales', 'Finance']
filename = "employees.csv"
print(f"Generating {filename}...")
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['ID', 'Name', 'Department', 'Salary']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for i in range(1, 21):
writer.writerow({
'ID': f'EMP{i:03d}',
'Name': f'Employee {i}',
'Department': random.choice(departments),
'Salary': random.randint(50000, 120000)
})
print(f"Successfully created {filename} with 20 records.")
if __name__ == "__main__":
generate_mock_data()
```
3. Run the generated code to verify.
a. Copy the generated Python code block and save it as `generate_data.py`.
b. Open the Terminal, navigate to the folder, and run the following command:
```python
python3 generate_data.py
```
c. Check your current folder. You should see a new file named `employees.csv`. Open it to verify the generated mock data.
![Dev bot result verification](/images/manual/use-cases/dev-bot-result.png#bordered)
### Real-time news analyst
Build an assistant that keeps you updated with the latest technology trends. By using the Website Crawler plug-in, this assistant can read live news sites and provide instant summaries of what's happening right now.
#### Configurations
- **Name**: `Daily Tech Digest`
- **Role prompt**:
```
You are a tech news reporter. When I send you a news site URL,
read the headlines and summarize the latest top five stories for me.
Limit the list to five.
```
- **Language model**: `qwen2.5:7b`
:::info
`qwen2.5:7b` excels at various NLP tasks such as contextual understanding and content writing. It is also compatible with functional calling, so you can install LobeChat plug-ins for enhanced capabilities.
:::
- **LobeChat plug-in**: "Website Crawler", which is used to access live web pages and analyze real-time content from provided URLs
:::info How Website Crawler works (Real-time vs. Offline)
Standard local AI models are offline and rely on pre-trained data from the past. The Website Crawler plug-in, specifically the getWebsiteContent function, acts as a bridge to the live internet.
When you provide a URL, the plug-in instantly accesses the web page in real time via an API, fetches the current content, and feeds it to the AI. This ensures that the AI model is accessing the latest live web content rather than using the old memory.
:::
![Install LobeChat plug-in Website Crawler](/images/manual/use-cases/install-website-crawler.png#bordered)
#### Interaction
1. In the basic interaction area, hover over the plug-in icon to ensure that the **Website Crawler** plug-in is enabled.
2. Send the URL address to the chat. For example, `https://github.com/trending`.
3. Paste and send the URL to the chat. The assistant lists specific news stories with summaries.
## FAQ
### Why did the connection check fail when I connected to Ollama?
This usually happens due to network or authentication settings. Follow these steps to resolve it:
1. Open Settings, and ensure that the **Authentication level** for Ollama is set to **Internal**.
2. Turn on the VPN in the LarePass desktop client, and confirm that the status changes to **Intranet** or **P2P**.
3. Run the connection check again.
:::tip
If you are on the same local network as your device and the **Authentication level** for Ollama is set to **None**, you do not need to enable the LarePass VPN.
:::
For more information, see [Ensure network connectivity](ollama.md#ensure-network-connectivity).

View File

@@ -0,0 +1,144 @@
---
outline: [2, 4]
description: Learn how to install and configure PDFMathTranslate on Olares. This tutorial guides you through using the local AI model Ollama to translate scientific PDFs, preserving original layouts and mathematical formulas.
---
# Translate scientific PDFs while preserving layout
PDFMathTranslate is an application designed to translate scientific PDF documents while retaining the original layout and mathematical formulas.
This tutorial provides instructions on how to install and use PDFMathTranslate on Olares, using the local AI model Ollama for translation.
## Learning objectives
By the end of this tutorial, you are able to:
- Configure the local AI model for PDF translation.
- Translate a scientific PDF and manage the output files.
## Prerequisites
Before you begin, make sure:
- Ollama is installed and running in your Olares environment.
- At least one model is installed using Ollama. For more information, see [Ollama](ollama.md).
## Install PDFMathTranslate
1. Open the Olares Market and search for "PDFMathTranslate".
2. Click **Get**, and then click **Install**.
![Install PDFMathTranslate](/images/manual/use-cases/install-pdfmathtranslate.png#bordered)
3. When the installation finishes, click **Open**. The PDFMathTranslate workspace is displayed.
![Open PDFMathTranslate](/images/manual/use-cases/open-pdfmathtranslate.png#bordered)
## Translate
### Upload your PDF document
:::warning PDF format requirements
Ensure that the PDF file is a standard PDF document that is not password-protected or corrupted. The application cannot process invalid PDF files.
:::
In the **File** area, select your input **Type**:
- If you select **File**, drag and drop your PDF document into the upload area, or click the area to browse your local storage.
When the document is uploaded, a preview of it appears in the **Preview** pane on the right.
- If you select **Link**, enter the link address of the PDF document.
The **Preview** pane remains blank during this step. The document content only appears in this area after the translation is completed.
### Configure the translation service
To use the local AI service Ollama for translation, configure the following settings:
1. From the **Service** list, select **Ollama**.
2. (Optional) To obtain the Ollama host address:
a. Go to Olares **Settings** > **Application** > **Ollama**.
b. In the **Entrances** section, click **Ollama API**.
c. Click **Set up endpoint**, and then click <i class="material-symbols-outlined">content_copy</i> to copy the endpoint address.
![Obtain Ollama host address](/images/manual/use-cases/copy-localhost-address.png#bordered){width=60%}
3. In the **OLLAMA_HOST** field, enter the Ollama host address.
4. In the **OLLAMA_MODEL** field, enter the exact identifier of the Ollama model you have downloaded. For example, `gemma3:4b`.
![Open PDFMathTranslate](/images/manual/use-cases/local-model-setup.png#bordered)
### Select languages and scope
1. Select the source and target languages:
a. **Translate from** indicates the original document's language.
b. **Translate to** indicates the language you want to read.
:::info
PDFMathTranslate does not auto-detect the languages. You must select them manually. Supported languages include English, Simplified Chinese, Traditional Chinese, French, German, Japanese, Korean, Russian, Spanish, and Italian.
:::
2. Specify which pages to translate:
* **All**: Translates the entire document.
* **First**: Translates only the first page.
* **First 5 pages**: Translates the first five pages.
* **Others**: Translates a custom range of pages.
![Set translation scope in PDFMathTranslate](/images/manual/use-cases/set-translation-scope.png#bordered)
3. Click **Translate**. The translation starts immediately.
:::warning
Do not click the **Cancel** button during translation. This might cause the process to report an error.
:::
### Download your files
When the translation is completed, the translated file is displayed in the **Preview** pane, and the application generates three files:
- Original source file
- Translated file
- Bilingual version
You can find these files in the Files app. To download them to your local computer, download them directly from the PDFMathTranslate workspace, or use the Files app.
#### In PDFMathTranslate workspace
On the left side of the pdfmathtranslate workspace, in the **Translated** section, click the download button next to the file.
![Download files translated in PDFMathTranslate](/images/manual/use-cases/download-translated-files.png#bordered)
#### From Olares Files app
1. Open the Files app, and then go to **Data** > **pdfmathtranslate** > **pdfmathtranslate**.
![Access files translated by PDFMathTranslate](/images/manual/use-cases/access-translated-files.png#bordered)
2. Double-click a file, and then click the download icon in the upper-right corner.
![Download files translated from Olares Files](/images/manual/use-cases/download-in-files.png#bordered)
## FAQ
### Why did the translation progress bar disappear?
If you refresh the page while a translation is running, the progress bar might disappear from the screen. This is a display issue only, and the translation is still processing in the background.
Wait for it to finish or check the output folder.
### Will the app keep multiple versions of the same file?
If you translate the same file name multiple times, the system replaces the previous version with the new one. It does not create numbered copies such as `file_1.pdf`. To keep multiple versions, rename your source file before translating it again.
### What should I do if the app becomes unresponsive or takes unusually long?
If the translation takes significantly longer than usual or the application stops responding, the background process might have stalled. To resolve this issue, uninstall and then re-install pdfmathtranslate.
### How do I perform a clean uninstallation of PDFMathTranslate?
To completely remove the application and its data:
1. Uninstall the app from Market or Desktop. For more information, see [Uninstall applications](../manual/olares/market/market.md#uninstall-applications).
2. Open the Files app, go to **Application** > **Data**, and then delete the `pdfmathtranslate` folder.

View File

@@ -0,0 +1,237 @@
---
outline: [2, 5]
description: A step-by-step guide to installing and using Stirling PDF on Olares. Learn how to securely manage PDF documents, covering essential tasks such as merging, editing, format conversion, and creating automated workflow pipelines.
---
# Manage PDF documents with Stirling PDF
Stirling PDF is a robust, locally hosted tool designed to manage PDF documents securely and efficiently.
## Learning objectives
By the end of this tutorial, you are able to:
* Redact (black out) sensitive information securely.
* Organize documents by merging, rotating, splitting, and re-ordering pages.
* Convert documents between PDF and other formats.
* Create automated workflows to batch process PDF documents.
## Install Stirling PDF
1. Open the Olares Market and search for "Stirling-PDF".
![Search for Stirling PDF from Market](/images/manual/use-cases/install-stirling-pdf.png#bordered)
2. Click **Get**, and then click **Install**.
3. After the installation is completed, open Stirling-PDF from Launchpad. The Stirling PDF homepage automatically launches in your default web browser.
![Stirling PDF homepage](/images/manual/use-cases/stirlingpdf-landing.png#bordered)
## Redact sensitive information
The **Manual Redaction** tool allows you to hide or black out sensitive information from your documents. You can scrub specific text, visual areas, or entire pages.
1. From the homepage, click **Manual Redaction**.
![Manual Redaction in Stirling PDF](/images/manual/use-cases/manual-redaction.png#bordered)
2. Upload your PDF file.
3. To redact specific text:
a. Click the **Text-based Redaction** icon on the toolbar.
![Text-based redaction icon](/images/manual/use-cases/text-based-redaction-icon.png#bordered){width=65%}
b. Select the text you want to hide.
c. (Optional) Click the **Colour Picker** icon to change the redaction color. By default, it is black.
![Color picker icon](/images/manual/use-cases/color-picker-icon.png#bordered){width=65%}
d. Click the **Apply changes** icon to confirm your selection.
![Checkmark confirm icon](/images/manual/use-cases/green-check-icon.png#bordered){width=65%}
4. To redact images or areas:
a. Click the **Box draw redaction** icon on the toolbar.
![Box draw redaction icon](/images/manual/use-cases/box-draw-redaction.png#bordered){width=65%}
b. Drag to draw a box around the sensitive area. When you release your mouse, the box turns from red to green, which indicates the area is selected successfully.
c. (Optional) Click the **Colour Picker** icon to change the redaction color. By default, it is black.
![Color picker icon](/images/manual/use-cases/color-picker-icon.png#bordered){width=65%}
5. To redact entire pages:
a. Click the **Page-based Redaction** icon on the toolbar.
![Page based redaction](/images/manual/use-cases/page-based-redaction.png#bordered){width=25%}
b. Enter the specific page numbers or ranges.
c. Select the redaction color.
d. Click **Apply**.
6. Export the redacted file.
a. Click the **Export** icon to download the sanitized PDF.
![Color picker icon](/images/manual/use-cases/export-pdf.png#bordered){width=25%}
b. Review your file to ensure that the redaction is correctly implemented.
The following image shows an example of a paragraph redacted in orange:
![Example of text redacted in orange](/images/manual/use-cases/text-redact-example.png#bordered){width=55%}
## Organize pages
The **Multi Tool** allows you to adjust the structure of the PDF documents, including merging, splitting, rotating, and re-ordering pages.
1. From the homepage, click **PDF Multi Tool**.
![Multi tool menu option on homepage](/images/manual/use-cases/pdf-multi-tool.png#bordered)
2. Click the **Add File** icon to add one or multiple PDF files.
![Multi tool add files](/images/manual/use-cases/add-pdf.png#bordered){width=65%}
3. To rotate pages:
- To rotate a single page, hover over the specific page thumbnail and click the **Rotate Left** icon or the **Rotate Right** icon.
![Rotate a single page](/images/manual/use-cases/rotate-single-page.png#bordered){width=65%}
- To rotate all pages at once, click the **Rotate Left** icon or the **Rotate Right** icon in the **File Name** area.
![Batch rotate](/images/manual/use-cases/batch-rotate.png#bordered){width=65%}
4. To re-order pages, drag the page to its new position in the grid.
5. To delete pages:
- To delete a single page, hover over the page, and then click the **Delete** icon on the thumbnail.
![Delete a single page](/images/manual/use-cases/delete-single-page.png#bordered){width=65%}
- To delete multiple pages, click the **Page Select** icon in the **File Name** area, select the checkboxes for all target pages, and then click **Delete Selected**.
![Batch delete](/images/manual/use-cases/page-select.png#bordered){width=65%}
6. To split pages:
- To split a specific page into a new file, click the **Scissors** icon on the left of that page's thumbnail.
![Split a single page](/images/manual/use-cases/split-single-page.png#bordered){width=65%}
- To split every page into its own separate file, click the **Scissors** icon in the **File Name** area.
![Split all pages](/images/manual/use-cases/split-all-page.png#bordered){width=65%}
7. To export pages:
- To download the re-organized file as a single PDF, click **Export**.
![Export all pages](/images/manual/use-cases/export-all-page.png#bordered){width=65%}
- To export specific pages, select the pages, and then click **Export Selected**.
![Export specific page](/images/manual/use-cases/export-selected-page.png#bordered){width=65%}
## Convert file formats
Stirling PDF supports conversion for over 50 file types, which allows you to turn images into PDFs or PDFs into other formats.
1. Find the convert section on the homepage, and click your specific convertion task.
![Convert menu option on homepage](/images/manual/use-cases/select-conversion.png#bordered)
2. Upload your source file.
3. Configure the available settings, such as color mode or layout preferences. The settings might vary by file type.
4. Click **Convert**. Wait for the process to finish. The converted file is downloaded automatically.
## Read and annotate
Use this feature to review documents and add notes or highlights.
1. From the homepage, click **View/Edit PDF**.
![View and edit pdf](/images/manual/use-cases/view-edit-pdf.png#bordered)
2. Click the **Open File** icon in the top right corner to load your document.
![Open pdf icon](/images/manual/use-cases/open-file.png#bordered){width=38%}
3. To add text annotations:
a. Click the **Text** icon and select your preferred font color and size.
![Add text annotations](/images/manual/use-cases/text-annotation.png#bordered){width=38%}
b. Click on the document where you want the note to appear, and then type your comment.
c. To delete a note, click the text box and click <i class="material-symbols-outlined">delete</i>.
4. To highlight or draw freehand:
a. Click Draw and adjust the brush color, thickness, and opacity.
![Draw lines to highlight](/images/manual/use-cases/draw-line.png#bordered){width=38%}
b. Click and drag on the document to apply your markings.
5. To finalize your edits, click the **Save** icon. The document containing your annotations is automatically downloaded.
![Save annotations](/images/manual/use-cases/save-edits.png#bordered){width=38%}
## Automate multi-step workflows
The Pipeline feature functions like a batch processing workflow for PDFs.
Instead of performing tasks one by one, you can create a custom pipeline to execute multiple actions in a single sequence. This scenario demonstrates the feature by merging documents and adding a password.
:::info
The Pipeline feature is currently in Beta. You might encounter occasional instability or interface changes. It is recommended to verify the final output when processing critical documents.
:::
### Create a pipeline
1. From the homepage, in the **Advanced** section, click **Pipeline**.
![Pipeline menu option on homepage](/images/manual/use-cases/pipeline.png#bordered)
2. Click **Configure**.
![Pipeline](/images/manual/use-cases/pipeline-menu.png#bordered){width=65%}
3. In the **Pipeline Name** box, enter a unique name for your workflow. For example, `Monthly invoice combination`.
4. Add and configure the merge operation:
a. From the **Select Operation** list, select **merge-pdfs**, and then click **Add operation**. The operation is added in the **Pipeline** area and related settings are displayed under it.
b. Define the specific parameters for the **merge-pdfs** operation, and then click **Save Operation Settings**.
![Add and configure an operation for pipeline](/images/manual/use-cases/merge-pdf.png#bordered){width=45%}
5. Add and configure the adding password operation:
a. From the **Select Operation** list, select **add-password**, and then click **Add operation**.
b. Define the specific parameters for the **add-password** operation, and then click **Save Operation Settings**.
6. Click **Save to Browser** to save your pipeline.
7. Close the **Pipeline Configuration** window, and then refresh the **Pipeline Menu** page.
### Run the pipeline
1. On the **Pipeline Menu** page, select the newly created pipeline "Monthly invoice combination".
![Select pipeline to use](/images/manual/use-cases/select-pipeline.png#bordered){width=65%}
2. Upload your PDF files, and then click **Submit**.
Stirling PDF automatically merges the files and secures the final document in one step. When the batch processing is completed, the file is downloaded automatically.
3. Double-click the downloaded file and enter the password you specified during the pipeline configuration to open it.
4. Verify that the files are merged and secured as expected.

View File

@@ -1,23 +1,14 @@
---
description: 通过最佳实践与进阶指南,深入挖掘 Olares 的性能、安全与高级定制能力。.
---
# 进阶篇:Olares 最佳实践与高级教程
# Olares 教程
本节提供深入的操作指南与经验证的最佳实践,帮助你在 Olares 上获得更高性能、更强安全性及更灵活的高级定制。无论是优化环境、保护数据,还是满足高阶工作流需求,这些资源都可作为初始部署之外的实战参考。
本节提供深入的操作指南与经验证的最佳实践,帮助你在 Olares 上获得更高性能、更强安全性及更灵活的高级定制。
- [为 Olares 配置自定义域名](set-custom-domain.md)
- [安装多节点 Olares 集群](install-olares-multi-node.md)
- [使用 Wise 构建知识中心](organize-content.md)
- [设置 SMTP](set-up-SMTP-service.md)
- [扩展 Olares 存储空间](expand-storage-in-olares.md)
- [安装 Olares 镜像及配置显卡直通](install-olares-gpu-passthrough.md)
- [本地访问 Olares 服务](local-access.md)

View File

@@ -0,0 +1,250 @@
---
outline: [2,3]
description: Learn the different methods to access Olares services locally for improved speed and offline capability.
---
# 本地访问 Olares 服务
Olares 的设计初衷是让你随时随地都能无缝访问自己的服务。
然而,通过内网直接访问设备具有显著优势:
- **极致性能**:在本地网络上传输文件时不受互联网延迟和瓶颈的影响。
- **隐私保护**:将流量严格限制在家庭网络内以增加安全性。
- **离线可用**:即使互联网服务不可用,也可以访问数据和应用。
## 目标
通过本教程,你将学会如何:
- 使用 LarePass VPN 建立安全、高速的本地连接。
- 使用 `.local` 域名访问 Olares 服务。
- 配置本地 DNS使全网设备可通过标准 URL 进行本地访问。
- 手动修改 Hosts 文件,确保特定计算机在无外网环境下也能访问。
## 选择连接方式
有四种建立本地连接的方法:
* **[方法 1启用 LarePass VPN](#方法-1-启用-larepass-vpn)**<br/>
利用 LarePass VPN 自动检测本地网络并优化连接速度,无需更改任何设置。
* **[方法 2使用 `.local` 域名](#方法-2-使用-local-域名)**<br/>
通过特定的本地 URL 格式访问设备。无需安装任何软件。
* **[方法 3配置本地 DNS](#方法-3-配置本地-dns)**<br/>
更新路由器或计算机的 DNS 设置,将标准 Olares URL 映射到本地 IP 地址。
* **[方法 4修改 Hosts 文件](#方法-4-修改-hosts-文件)**<br/>
在单台计算机上手动将标准 Olares URL 映射到本地 IP。
## 方法 1启用 LarePass VPN
LarePass VPN 旨在兼顾连接安全与性能优化。启用后LarePass 会自动检测设备是否处于同一网络,并切换至**内网**模式。
:::tip 始终启用 VPN 以进行远程访问
保持 LarePass VPN 启用。它会自动优先选择最快的可用路由,确保你无需手动切换即可获得最佳速度。
:::
:::info iOS 和 macOS 设置
在 iOS 或 macOS 上首次启用该功能时,系统可能会提示你添加 VPN 配置文件。允许此操作以完成设置。
:::
直接在你当前用于访问 Olares 的设备上启用 LarePass VPN。
<tabs>
<template #使用-LarePass-移动端>
1. 打开 LarePass 应用,进入**设置**。
2. 在**我的 Olares** 卡片中,打开 VPN 开关。
![移动端开启 LarePass VPN](/images/zh/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #使用-LarePass-桌面端>
1. 打开 LarePass 应用,点击左上角的头像打开用户菜单。
2. 打开**专用网络连接**开关。
![桌面端开启 LarePass VPN](/images/zh/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
启用后,可以查看 LarePass 中的网络状态以确认连接类型:
| 状态 | 描述 |
|:-----------|:---------------------------|
| **内网** | 通过本地局域网 IP 直连。速度最快。 |
| **P2P** | 设备间的直接加密隧道。连接速度快。 |
| **DERP** | 通过安全中继服务器路由,仅在无法直连时作为备用方案。 |
## 方法 2使用 `.local` 域名
若不希望安装额外应用,可使用 `.local` 域名访问服务。根据操作系统不同,有两种域名格式。
:::info 使用 HTTP 协议
`.local` 域名不支持 HTTPS。务必在 URL 开头显式添加 `http://`
:::
### 单级域名(所有操作系统适用)
:::warning 仅支持社区应用
Desktop 和文件管理器等 Olares 系统应用不支持此 URL 格式,因此无法正确加载。
:::
此格式通过连字符(`-`)连接入口 ID 和用户名来使用单级主机名。
- **默认 URL**
```plain
https://<entrance_id>.<username>.olares.cn
```
- **本地访问 URL**
```plain
http://<entrance_id>-<username>-olares.local
```
### 多级域名 (macOS 和 iOS 适用)
Apple 设备支持通过 [Bonjour](https://developer.apple.com/bonjour/)(零配置网络)进行本地服务发现,因此能够在 macOS 和 iOS 上解析 `.local` 下的多级域名。这使得本地 URL 格式可以与默认的远程访问地址保持结构一致。
- **默认 URL**
```plain
https://<entrance_id>.<username>.olares.cn
```
- **本地访问 URL**
```plain
http://<entrance_id>.<username>.olares.local
```
![多级域名](/images/manual/get-started/multilevel-local-domain-mac.png#bordered)
## 方法 3配置本地 DNS
为了获得无缝体验(即标准 URL 自动解析为你的本地 IP 地址),你可以配置网络 DNS。此配置确保网络上所有设备的访问一致无需单独设置客户端。
### 查找 Olares 设备的内网 IP
要配置 DNS首先需要找到 Olares 设备的内网 IP。
<tabs>
<template #通过-LarePass-手机端查看>
如果你的手机和 Olares 设备处于同一网络:
1. 打开 LarePass 应用,点击**设置** > **系统**,进入 **Olares 管理**页面。
![点击 System 卡片](/images/zh/manual/get-started/larepass-system.png#bordered)
2. 点击设备卡片。
![点击设备卡片](/images/zh/manual/get-started/larepass-device-card.png#bordered)
3. 并向下滚动到**网络**部分。可以在此处找到**内网 IP**。
![找到网络部分](/images/zh/manual/get-started/larepass-network.png#bordered)
</template>
<template #通过-Olares-终端查看>
控制面板提供了内置终端,允许你直接从浏览器运行系统命令,无需外部 SSH 客户端。
1. 打开控制面板,在左侧导航栏的**终端**下选择 **Olares**。
![从控制面板查找内网 IP](/images/zh/manual/get-started/find-internal-ip-from-controlhub.png#bordered)
2. 在终端输入 `ifconfig` 并按下 **Enter** 确认。
3. 寻找你的活动连接,通常命名为 `enp3s0`(有线)或 `wlo1`无线。IP 地址位于 `inet` 之后。
输出示例:
```bash
enp3s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.50.116 netmask 255.255.255.0 broadcast 192.168.50.255
inet6 fe80::4194:4045:c35e:7b32 prefixlen 64 scopeid 0x20<link>
ether d8:43:ae:54:ce:fc txqueuelen 1000 (Ethernet)
RX packets 80655321 bytes 71481515308 (71.4 GB)
RX errors 0 dropped 136 overruns 0 frame 0
TX packets 51867817 bytes 15924740708 (15.9 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
```
在此示例中,`192.168.50.116` 即为内网 IP。
</template>
</tabs>
### 配置 DNS
确定内网 IP 地址后,现在必须配置 DNS 设置以正确路由流量。你可以将此配置应用于单台电脑以供个人访问,或者更新路由器以实现网络上所有设备的无缝本地解析。
<tabs>
<template #设置单台设备>
更新特定电脑上的 DNS 设置。以 macOS 为例:
1. 打开 Apple 菜单,进入**系统设置**。
2. 选择 **Wi-Fi**,然后点击已连接网络的**详细信息**。
3. 选择 **DNS** 并更新服务器列表:
a. 点击 **DNS 服务器**下的 **+** 按钮,添加 Olares 设备的内网 IP例如 `192.168.x.x`)。
b. 确保 Olares IP 列在顶部。在其下方添加你原来的 DNS或 `1.1.1.1`)作为备用。<br/>这确保了如果 Olares 设备关机,路由器会自动切换到辅助 DNS保持互联网连接正常。
4. 点击**好**保存更改。
</template>
<template #设置所有设备>
更新路由器上的 DNS将更改应用于网络中的所有设备。
1. 登录路由器的管理面板。
2. 导航至 **DHCP / DNS 设置**。
3. 将**首选 DNS** 设置为 Olares 设备的内网 IP例如 `192.168.x.x`)。
4. 将**备用 DNS** 设置为当前的首选 DNS或公共提供商如 `1.1.1.1`)。<br/>这确保了如果 Olares 设备关机,路由器会自动切换到辅助 DNS保持互联网连接正常。
5. 保存并重新连接设备以刷新 DNS 缓存。
</template>
</tabs>
配置完成后,可以使用标准公网地址和本地地址访问 Olares。
:::tip
你可以从 Olares 应用市场安装 AdGuard Home以图形化方式监控流量并管理 DNS 映射。
:::
## 方法 4修改 hosts 文件
如果无法更改路由器设置且需要在特定电脑上立即离线访问,可以在 hosts 文件中手动映射域名。
1. 找到 hosts 文件:
- **Windows**`C:\Windows\System32\drivers\etc\hosts`
- **macOS/Linux**`/etc/hosts`
2. 使用文本编辑器打开文件(需要管理员权限)。
3. 添加映射行:
```plain
# 替换为实际内网 IP 和用户名
# Olares 应用
192.168.31.208 desktop.<username>.olares.cn
192.168.31.208 auth.<username>.olares.cn
192.168.31.208 files.<username>.olares.cn
192.168.31.208 market.<username>.olares.cn
192.168.31.208 settings.<username>.olares.cn
192.168.31.208 dashboard.<username>.olares.cn
192.168.31.208 control-hub.<username>.olares.cn
192.168.31.208 profile.<username>.olares.cn
192.168.31.208 vault.<username>.olares.cn
# 根据需要添加其他社区应用
192.168.31.208 <entrance_id>.<username>.olares.cn
```
4. 保存文件以应用更改。这能确保即使在断网情况下,你也能进行本地访问。
你可以通过检查 URL 加载速度或使用终端来验证更改。例如:
```bash
ping desktop.<username>.olares.cn
```
如果返回的 IP 地址以 `192.168` 开头,即表示配置成功。
## 常见问题
### 为什么在 Mac 上无法再启用 LarePass VPN
如果之前成功启用过 VPN但现在停止工作可能需要重置系统扩展。
:::info
根据 macOS 版本不同,界面可能略有差异。
:::
1. 打开**系统设置**,搜索`扩展`,选择**登录项与扩展**。
2. 滚动到**网络扩展** 部分,点击信息图标 (ⓘ) 查看已加载的扩展。
3. 找到 LarePass点击三个点 (...),选择**删除扩展**。
4. 确认卸载。
5. 重启 Mac 并在 LarePass 桌面客户端中重新启用 VPN。
### 为什么我在 Windows 上无法启用 LarePass VPN
第三方杀毒软件可能会错误地将 LarePass 桌面客户端标记为可疑,从而阻止其启动 VPN 服务。
如果在首次打开 LarePass 时收到杀毒软件提示,请允许应用程序继续运行。
如果 VPN 仍然无法启用:
1. 打开安全软件,检查 LarePass 是否被拦截。
2. 将 LarePass 主程序添加到杀毒软件的白名单或排除项中。
3. 重启 LarePass 并启用 VPN。
### 为什么在 macOS 上Chrome 无法访问 `.local` 域名?
如果 macOS 未授予局域网访问权限Chrome 可能会无法访问本地 URL。
要启用访问权限:
1. 打开 Apple 菜单,进入**系统设置**。
2. 进入**隐私与安全性** > **局域网**。
3. 在列表中找到 Google Chrome 和 Google Chrome Helper并开启开关。
![启用局域网权限](/images/manual/larepass/mac-chrome-local-access.png#bordered){width=400}
4. 重启 Chrome 并再次尝试访问本地 URL。
### 为什么在 Chrome 上使用 `.local` 域名时,应用无法在 iFrame 中加载 (macOS)
使用本地域名时Chrome 可能会默认使用 HTTPS你可能会看到“连接不安全”的警告。
![本地地址错误](/images/manual/get-started/incorrect-local-address.png#bordered)
要解决此问题,在 URL 开头显式添加 HTTP 协议头 (`http://`),告诉浏览器这是一个仅在本地网络中使用的链接。

View File

@@ -1,34 +1,33 @@
---
outline: [2,3]
description: 了解如何通过局域网 (LAN) 直接访问 Olares 应用和服务,以获得最快速度、隐私保护和离线可靠性
description: 了解如何使用 LarePass VPN 安全访问 Olares 服务
---
# 内网访问 Olares 服务
# 使用 LarePass VPN 安全访问 Olares 服务
通常你可以通过浏览器使用 URL例如 `https://desktop.<username>.olares.com`)来访问 Olares 服务。这种方式让你可以随时随地通过任何设备访问服务。
通常,你会使用类似 `https://desktop.<username>.olares.cn` 的网址在浏览器中访问 Olares。这种方式允许你在任何设备上随时随地访问服务。
然而,通过内网直接访问设备具有显著优势
- **极致性能**:在本地网络上传输文件时不受互联网延迟和瓶颈的影响
- **隐私保护**:将流量严格限制在家庭网络内以增加安全性
- **离线可用**:即使互联网服务不可用,也可以访问数据和应用。
虽然该地址可从任意网络访问,但为了获得更安全、高效的连接,建议启用 LarePass VPN。客户端会自动检测网络环境并智能切换连接模式
- **居家**:建立直连内网通道,大幅提升局域网文件传输速度
- **远程**:切换至加密安全隧道,保障远程访问的数据安全
本文档介绍了建立本地连接的几种方法:
- [启用 LarePass VPN推荐](#方法-1-启用-larepass-vpn)<br/>这是最简单的方案,自动建立最快连接,无需手动配置
- [使用 `.local` 域名](#方法-2-使用-local-域名)<br/>此方法无需安装,但需根据操作系统使用特定的 URL 格式
- [配置本地 DNS进阶](#方法-3-配置本地-dns)<br/>此方法通过更新路由器或单台电脑的 DNS 设置,支持使用标准 URL 进行本地访问
- [修改 Host 文件(备选)](#方法-4-修改-hosts-文件)<br/>此方法在单台电脑上手动将标准 URL 映射到本地 IP确保在无外网连接时也能访问。
## 下载 LarePass
要使用安全 VPN 连接,需先在当前设备上安装 LarePass 客户端
- **移动端**:使用在创建 Olares ID 过程中安装的 LarePass 移动端
- **桌面端**:下载并安装 LarePass 桌面客户端
## 方法 1启用 LarePass VPN
无论是在设备旁还是在旅途中,连接的最稳健方式都是使用 LarePass VPN。它能智能检测你是否处于同一网络并切换到直接的**内网**模式以获得最大速度
1. 访问 <AppLinkCN />。
2. 下载对应操作系统的版本
## 启用 LarePass VPN
安装完成后,在当前设备上启用 VPN。
:::tip 始终启用 VPN 以进行远程访问
保持 LarePass VPN 启用。它会自动优先选择最快的可用路由,确保你无需手动切换即可获得最速度。
建议保持 LarePass VPN 开启。系统会自动优选最佳路由,你无需手动切换即可确保持续获得最速度。
:::
:::info iOS 和 macOS 设置
在 iOS 或 macOS 上首次启用该功能时,系统可能会提示你添加 VPN 配置文件。允许此操作以完成设置。
首次在 iOS 或 macOS 上开启此功能时,系统可能会弹窗请求添加 VPN 配置。允许此操作以完成设置。
:::
直接在你当前用于访问 Olares 的设备上启用 LarePass VPN。
<tabs>
<template #使用-LarePass-移动端>
@@ -46,7 +45,8 @@ description: 了解如何通过局域网 (LAN) 直接访问 Olares 应用和服
</template>
</tabs>
启用后,可以查看 LarePass 中的网络状态以确认连接类型:
## 确认连接状态
开启后,查看 LarePass 中的状态标签来确认当前的连接模式:
| 状态 | 描述 |
|:-----------|:---------------------------|
@@ -54,155 +54,13 @@ description: 了解如何通过局域网 (LAN) 直接访问 Olares 应用和服
| **P2P** | 设备间的直接加密隧道。连接速度快。 |
| **DERP** | 通过安全中继服务器路由,仅在无法直连时作为备用方案。 |
## 方法 2使用 `.local` 域名
如果不想使用 VPN可以使用 `.local` 域名访问服务。根据兼容性需求,有两种域名格式可用。
### 单级域名(所有操作系统适用)
:::warning 仅支持社区应用
Desktop 和文件管理器等 Olares 系统应用不支持此 URL 格式,因此无法正确加载。
:::
此格式通过连字符(`-`)连接入口 ID 和用户名来使用单级主机名。
- **默认 URL**:
```plain
https://<entrance_id>.<username>.olares.cn
```
- **本地访问 URL**:
```plain
http://<entrance_id>-<username>-olares.local
```
### 多级域名 (macOS 和 iOS 适用)
Apple 设备支持通过 [Bonjour](https://developer.apple.com/bonjour/)(零配置网络)进行本地服务发现,因此能够在 macOS 和 iOS 上解析 `.local` 下的多级域名。这使得本地 URL 格式可以与默认的远程访问地址保持结构一致。
- **默认 URL**:
```plain
https://<entrance_id>.<username>.olares.cn
```
- **本地访问 URL**:
```plain
http://<entrance_id>.<username>.olares.local
```
![多级域名](/images/manual/get-started/multilevel-local-domain-mac.png#bordered)
## 方法 3配置本地 DNS
为了获得无缝体验(即标准 URL 自动解析为你的本地 IP 地址),你可以配置网络 DNS。此配置确保网络上所有设备的访问一致无需单独设置客户端。
### 查找 Olares 设备的内网 IP
要配置 DNS首先需要找到 Olares 设备的内网 IP。
<tabs>
<template #通过-LarePass-手机端查看>
如果你的手机和 Olares 设备处于同一网络:
1. 打开 LarePass 应用,点击**设置** > **系统**,进入 **Olares 管理**页面。
![点击 System 卡片](/images/zh/manual/get-started/larepass-system.png#bordered)
2. 点击设备卡片。
![点击设备卡片](/images/zh/manual/get-started/larepass-device-card.png#bordered)
3. 并向下滚动到**网络**部分。可以在此处找到**内网 IP**。
![找到网络部分](/images/zh/manual/get-started/larepass-network.png#bordered)
</template>
<template #通过-Olares-终端查看>
控制面板提供了内置终端,允许你直接从浏览器运行系统命令,无需外部 SSH 客户端。
1. 打开控制面板,在左侧导航栏的**终端**下选择 **Olares**。
![从控制面板查找内网 IP](/images/zh/manual/get-started/find-internal-ip-from-controlhub.png#bordered)
2. 在终端输入 `ifconfig` 并按下 **Enter** 确认。
3. 寻找你的活动连接,通常命名为 `enp3s0`(有线)或 `wlo1`无线。IP 地址位于 `inet` 之后。
输出示例:
```bash
enp3s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.50.116 netmask 255.255.255.0 broadcast 192.168.50.255
inet6 fe80::4194:4045:c35e:7b32 prefixlen 64 scopeid 0x20<link>
ether d8:43:ae:54:ce:fc txqueuelen 1000 (Ethernet)
RX packets 80655321 bytes 71481515308 (71.4 GB)
RX errors 0 dropped 136 overruns 0 frame 0
TX packets 51867817 bytes 15924740708 (15.9 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
```
在此示例中,`192.168.50.116` 即为内网 IP。
</template>
</tabs>
### 配置 DNS
确定内网 IP 地址后,现在必须配置 DNS 设置以正确路由流量。你可以将此配置应用于单台电脑以供个人访问,或者更新路由器以实现网络上所有设备的无缝本地解析。
<tabs>
<template #设置单台设备>
更新特定电脑上的 DNS 设置。以 macOS 为例:
1. 打开 Apple 菜单,进入**系统设置**。
2. 选择 **Wi-Fi**,然后点击已连接网络的**详细信息**。
3. 选择 **DNS** 并更新服务器列表:
a. 点击 **DNS 服务器**下的 **+** 按钮,添加 Olares 设备的内网 IP例如 `192.168.x.x`)。
b. 确保 Olares IP 列在顶部。在其下方添加你原来的 DNS或 `1.1.1.1`)作为备用。<br/>这确保了如果 Olares 设备关机,路由器会自动切换到辅助 DNS保持互联网连接正常。
4. 点击**好**保存更改。
</template>
<template #设置所有设备>
更新路由器上的 DNS将更改应用于网络中的所有设备。
1. 登录路由器的管理面板。
2. 导航至 **DHCP / DNS 设置**。
3. 将**首选 DNS** 设置为 Olares 设备的内网 IP例如 `192.168.x.x`)。
4. 将**备用 DNS** 设置为当前的首选 DNS或公共提供商如 `1.1.1.1`)。<br/>这确保了如果 Olares 设备关机,路由器会自动切换到辅助 DNS保持互联网连接正常。
5. 保存并重新连接设备以刷新 DNS 缓存。
</template>
</tabs>
配置完成后,可以使用标准公网地址和本地地址访问 Olares。
:::tip
你可以从 Olares 应用市场安装 AdGuard Home以图形化方式监控流量并管理 DNS 映射。
:::
## 方法 4修改 hosts 文件
如果无法更改路由器设置且需要在特定电脑上立即离线访问,可以在 hosts 文件中手动映射域名。
1. 找到 hosts 文件:
- **Windows:** `C:\Windows\System32\drivers\etc\hosts`
- **macOS/Linux:** `/etc/hosts`
2. 使用文本编辑器打开文件(需要管理员权限)。
3. 添加映射行:
```plain
# 替换为实际内网 IP 和用户名
# Olares 应用
192.168.31.208 desktop.<username>.olares.cn
192.168.31.208 auth.<username>.olares.cn
192.168.31.208 files.<username>.olares.cn
192.168.31.208 market.<username>.olares.cn
192.168.31.208 settings.<username>.olares.cn
192.168.31.208 dashboard.<username>.olares.cn
192.168.31.208 control-hub.<username>.olares.cn
192.168.31.208 profile.<username>.olares.cn
192.168.31.208 vault.<username>.olares.cn
# 根据需要添加其他社区应用
192.168.31.208 <entrance_id>.<username>.olares.cn
```
4. 保存文件以应用更改。这能确保即使在断网情况下,你也能进行本地访问。
你可以通过检查 URL 加载速度或使用终端来验证更改。例如:
```bash
ping desktop.<username>.olares.cn
```
如果返回的 IP 地址以 `192.168` 开头,即表示配置成功。
## 了解更多
- [通过 LarePass VPN 随时随地访问 Olares](../../../zh/manual/larepass/private-network.md): 了解如何使用 LarePass VPN。
- [网络](../../../zh/developer/concepts/network.md): 了解 Olares 中的应用的各类入口。
## 常见问题
### 为什么在 Mac 上无法再启用 LarePass VPN
如果之前成功启用过 VPN但现在停止工作可能需要重置系统扩展。
:::info
根据 macOS 版本不同,界面可能略有差异。
:::
1. 打开**系统设置**,搜索 “扩展",选择**登录项与扩展**。
1. 打开**系统设置**,搜索`扩展`,选择**登录项与扩展**。
2. 滚动到**网络扩展** 部分,点击信息图标 (ⓘ) 查看已加载的扩展。
3. 找到 LarePass点击三个点 (...),选择**删除扩展**。
4. 确认卸载。
@@ -218,18 +76,6 @@ ping desktop.<username>.olares.cn
2. 将 LarePass 主程序添加到杀毒软件的白名单或排除项中。
3. 重启 LarePass 并启用 VPN。
### 为什么在 macOS 上Chrome 无法访问 `.local` 域名?
如果 macOS 未授予局域网访问权限Chrome 可能会无法访问本地 URL
要启用访问权限:
1. 打开 Apple 菜单,进入**系统设置**。
2. 进入**隐私与安全性** > **局域网**。
3. 在列表中找到 Google Chrome 和 Google Chrome Helper并开启开关。
![启用局域网权限](/images/manual/larepass/mac-chrome-local-access.png#bordered){width=400}
4. 重启 Chrome 并再次尝试访问本地 URL。
### 为什么在 Chrome 上使用 `.local` 域名时,应用无法在 iFrame 中加载 (macOS)
使用本地域名时Chrome 可能会默认使用 HTTPS你可能会看到“连接不安全”的警告。
![本地地址错误](/images/manual/get-started/incorrect-local-address.png#bordered)
要解决此问题,在 URL 开头显式添加 HTTP 协议头 (`http://`),告诉浏览器这是一个仅在本地网络中使用的链接。
## 了解更多
- [本地访问 Olares 服务](../best-practices/local-access.md):了解所有 Olares 本地连接方式
- [网络](../../../zh/developer/concepts/network.md):了解 Olares 中的应用的各类入口。

Some files were not shown because too many files have changed in this diff Show More