add demo
|
|
@ -45,20 +45,20 @@
|
||||||
- ***无需联网使用,全开源***
|
- ***无需联网使用,全开源***
|
||||||
- ***零配置,无需注册,下载即用***
|
- ***零配置,无需注册,下载即用***
|
||||||
- ***零污染,无插件依赖***
|
- ***零污染,无插件依赖***
|
||||||
- ***精小,打包后仅65M,确包含了所有的办公套件***
|
- ***精小,打包后仅65M,却包含了所有的办公套件***
|
||||||
- ***可无限扩展,支持自定义应用***
|
- ***可无限扩展,支持自定义应用***
|
||||||
- ***golang开发后端,低资源消耗和高性能***
|
- ***golang开发后端,低资源消耗和高性能***
|
||||||
- ***支持多平台,Windows、Linux、MacOS***
|
- ***支持多平台,Windows、Linux、MacOS***
|
||||||
- ***只会html和拥有可执行的应用,简单学习一下应用商店配置即可开发出复杂的应用***
|
- ***完善的应用商店体系,简单学习一下[应用商店配置](./docs/Store.md)即可开发出复杂的应用***
|
||||||
|
|
||||||
## 🚧 开发进程
|
## 🚧 开发进程
|
||||||
- 2024年7月,发布v1.0.0版本,发布后,项目进入第二阶段。
|
- 2024年8月1日,发布v1.0.0版本,发布后,项目进入第二阶段。
|
||||||
|
|
||||||
## 🏭 第二阶段目标
|
## 🏭 第二阶段目标
|
||||||
- [x] 完善的应用商店体系
|
- [x] 完善的应用商店体系
|
||||||
- [x] 完善开发文档
|
- [x] 完善开发文档
|
||||||
- [x] 支持远程存储
|
- [x] 支持远程存储
|
||||||
- [x] 完成企业客户端
|
- [x] 开发更多的商店应用
|
||||||
|
|
||||||
|
|
||||||
## ⚡ 功能说明和预览
|
## ⚡ 功能说明和预览
|
||||||
|
|
|
||||||
133
docs/Store.md
|
|
@ -1,17 +1,27 @@
|
||||||
## 应用管理
|
## GodoOS应用商店开发教程
|
||||||
|
|
||||||
|
### 快速开始
|
||||||
|
1. 下载[mysql5.7的zip包](https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.44-winx64.zip),并解压到用户目录.godoos/run/windows/目录下,文件夹命名为mysql5.7
|
||||||
|
2. 复制本程序docs/demo/mysql5.7到用户目录.godoos/run/windows/mysql5.7目录下
|
||||||
|
3. 打开应用商店,添加应用,选择开发模式,本地路径输入mysql5.7,点击确定
|
||||||
|
|
||||||
|
### 开发条件
|
||||||
|
|
||||||
|
1. 会简单的html开发
|
||||||
|
2. 熟悉可执行文件的启动流程,然后根据如下的流程配置json文件
|
||||||
|
|
||||||
### 如何添加一个应用
|
### 如何添加一个应用
|
||||||
|
|
||||||
1. 在用户目录.godoos/run下建立一个应用文件夹。
|
1. 在用户目录.godoos/run/windows/下建立一个应用文件夹。
|
||||||
2. 一个应用需要包含两个配置文件,在应用根目录下创建`install.json`和`store.json`两个文件。 配置文件格式如下:
|
2. 一个应用需要包含两个配置文件,在应用根目录下创建`install.json`和`store.json`两个文件。 配置文件格式如下:
|
||||||
- install.json
|
- install.json [样本](./demo/mysql5.7/install.json)
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "", // string, 应用程序名称。
|
"name": "", // string, 应用程序名称。
|
||||||
"url": "", // string, 应用程序下载地址。
|
"url": "", // string, 应用程序下载地址。
|
||||||
"webUrl":"", // string, 如何设置,应用程序将显示到桌面。
|
"webUrl":"", // string, 如何设置,应用程序将显示到桌面。
|
||||||
"isDev": true, // boolean, 是否为开发环境。
|
"isDev": true, // boolean, 是否为开发环境。
|
||||||
"needDownload": true, // boolean, 是否需要下载。
|
"needDownload": true, // boolean, 是否需要下载,本地开发设置为false。
|
||||||
"needInstall": true, // boolean, 是否需要安装。
|
"needInstall": true, // boolean, 是否需要安装。
|
||||||
"version": "1.0.0", // string, 应用程序版本。
|
"version": "1.0.0", // string, 应用程序版本。
|
||||||
"icon": "", // string, 应用程序图标,为可访问的网络地址。
|
"icon": "", // string, 应用程序图标,为可访问的网络地址。
|
||||||
|
|
@ -21,9 +31,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
如果不需要后端进程的web应用,store.json可以不用配置。
|
- 备注:如果不需要后端进程的web应用,store.json可以不用配置。
|
||||||
|
|
||||||
install.json的struct为:
|
install.json的结构体为:
|
||||||
```json
|
```json
|
||||||
type InstallInfo struct {
|
type InstallInfo struct {
|
||||||
Name string `json:"name"` // 应用程序名称。重要,必须和应用的目录名称一致。
|
Name string `json:"name"` // 应用程序名称。重要,必须和应用的目录名称一致。
|
||||||
|
|
@ -42,7 +52,7 @@ type InstallInfo struct {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- store.json
|
- store.json [样本](./demo/mysql5.7/store.json)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -50,33 +60,38 @@ type InstallInfo struct {
|
||||||
"icon": "mysql.png", // string, 应用程序图标,一般放在应用程序static目录下。
|
"icon": "mysql.png", // string, 应用程序图标,一般放在应用程序static目录下。
|
||||||
"setting": {
|
"setting": {
|
||||||
"binPath": "{exePath}/bin/mysqld.exe", // string, 重要,必须设置。为启动程序路径。
|
"binPath": "{exePath}/bin/mysqld.exe", // string, 重要,必须设置。为启动程序路径。
|
||||||
"confPath": "{exePath}/my.ini",// string, 重要,必须设置。为配置文件路径。
|
"confPath": "{exePath}/my.ini",// string, 可为空。为配置文件路径。
|
||||||
"progressName": "mysqld.exe",// string, 进程名称。如果为单线程可不设置。
|
"progressName": "mysqld.exe",// string, 进程名称。如果为单线程可不设置。
|
||||||
"isOn": true // boolean, 是否启动守护进程
|
"isOn": true // boolean, 是否启动守护进程
|
||||||
},
|
},
|
||||||
"config": { // object, 配置文件。里面的配置可以任意填写,和cmds配合使用。
|
"config": { // object, 配置文件。里面的配置可以任意填写,和commands配合使用。可以通过http设置里面的参数。
|
||||||
},
|
},
|
||||||
"cmds": {},// object, 命令集。
|
"commands": {},// object, 命令列表集。可供install里的installCmds调用,也可以通过外部http请求调用。
|
||||||
"install": { // object, 安装配置。
|
"install": { // object, 安装配置。
|
||||||
"envs": [],// object[], 环境变量。
|
"installEnvs": [],// object[], 环境变量。
|
||||||
"cmds": []// object[], 启动命令。
|
"installCmds": []// object[], 启动命令。可调用命令列表集commands里面的命令。
|
||||||
},
|
},
|
||||||
"start": {// object, 启动配置。
|
"start": {
|
||||||
"envs": [],// object[], 环境变量。
|
"startEnvs": [],
|
||||||
"cmds": []// object[], 启动命令。
|
"startCmds": [// object[], 纯参数命令集。将启动`setting.binPath`,不可调用命令列表集`commands`里面的命令。
|
||||||
|
"--defaults-file={exePath}/my.ini"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- 备注:核心的替换参数为`{exePath}`即程序的执行目录。其他`{参数}`对应`store.json`里的`config`。
|
||||||
|
|
||||||
store.json的struct为
|
store.json的struct为
|
||||||
```json
|
```json
|
||||||
type StoreInfo struct {
|
type StoreInfo struct {
|
||||||
Name string `json:"name"` // 应用程序的名称。
|
Name string `json:"name"` // 应用程序商店的名称。
|
||||||
Icon string `json:"icon"` // 应用程序的图标路径。
|
Icon string `json:"icon"` // 应用程序商店的图标路径。
|
||||||
Setting Setting `json:"setting"` // 应用程序的配置信息。
|
Setting Setting `json:"setting"` // 应用程序商店的配置信息。
|
||||||
Config map[string]any `json:"config"` // 应用程序的配置信息映射。
|
Config map[string]any `json:"config"` // 应用程序的配置信息映射。
|
||||||
Cmds map[string][]Cmd `json:"cmds"` // 应用程序的命令集合。
|
Commands map[string][]Cmd `json:"commands"` // 应用程序的命令集合。
|
||||||
Install Install `json:"install"` // 安装应用程序的信息。
|
Install InstallStruct `json:"install"` // 安装应用程序的信息。
|
||||||
Start Install `json:"start"` // 启动应用程序的信息。
|
Start StartStruct `json:"start"` // 启动应用程序的信息。
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Setting的结构体为:
|
Setting的结构体为:
|
||||||
|
|
@ -103,12 +118,16 @@ type Cmd struct {
|
||||||
Envs []Item `json:"envs"` // 命令执行时的环境变量。
|
Envs []Item `json:"envs"` // 命令执行时的环境变量。
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Install的struct为:
|
Install的结构体为:
|
||||||
```json
|
```json
|
||||||
// Install 描述了安装过程中的环境变量和命令列表。
|
// InstallStruct 描述了安装过程中的环境变量和命令列表。
|
||||||
type Install struct {
|
type InstallStruct struct {
|
||||||
Envs []Item `json:"envs"` // 安装过程中需要的环境变量。
|
InstallEnvs []Item `json:"installEnvs"` // 安装过程中需要的环境变量。
|
||||||
Cmds []string `json:"cmds"` // 安装过程中需要执行的命令列表。
|
InstallCmds []string `json:"installCmds"` // 安装过程中需要执行的命令列表。
|
||||||
|
}
|
||||||
|
type StartStruct struct {
|
||||||
|
StartEnvs []Item `json:"startEnvs"` // 安装过程中需要的环境变量。
|
||||||
|
StartCmds []string `json:"startCmds"` // 安装过程中需要执行的命令列表。
|
||||||
}
|
}
|
||||||
// Item 是一个通用的键值对结构体,用于表示配置项或环境变量等。
|
// Item 是一个通用的键值对结构体,用于表示配置项或环境变量等。
|
||||||
type Item struct {
|
type Item struct {
|
||||||
|
|
@ -121,13 +140,13 @@ type Item struct {
|
||||||
|
|
||||||
### 配置文件`store.json`说明
|
### 配置文件`store.json`说明
|
||||||
|
|
||||||
1. `install`和`start`配置可以调用`cmds`里的命令。
|
1. `install`可以调用`commands`里的命令。
|
||||||
2. `cmds`里的命令也可以调用自身的命令。
|
2. `commands`里的命令也可以调用自身的命令。
|
||||||
3. 所有的命令都可以串联使用。
|
3. 所有的命令都可以串联使用。
|
||||||
|
|
||||||
### 如何设置配置
|
### 如何设置配置
|
||||||
1. 在应用目录下建立`static`目录,并创建`index.html`文件。`install.json`中`setting`设置为`true`。
|
1. 在应用目录下建立`static`目录,并创建`index.html`文件。`install.json`中`setting`设置为`true`。
|
||||||
前端配置样列
|
前端配置样列 [样本](./demo/mysql5.7/static/index.html)
|
||||||
```js
|
```js
|
||||||
const postData = {
|
const postData = {
|
||||||
dataDir: dataDir,// 此处对应store.json中的config配置项
|
dataDir: dataDir,// 此处对应store.json中的config配置项
|
||||||
|
|
@ -144,17 +163,67 @@ const comp = await fetch('http://localhost:56780/store/setting', {
|
||||||
body: JSON.stringify(postData)
|
body: JSON.stringify(postData)
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
关键要设置好`name`和`cmdKey`,`cmdKey`对应`store.json`中的`cmds`数组配置项中的`name`,一个数组中又可以配置一系列的命令,可以参考Cmd的结构体
|
- 关键要设置好`name`和`cmdKey`,`name`为应用名称,`cmdKey`对应`store.json`中的`commands`对象中的键,一个对象中又可以配置一系列的命令,可以参考Cmd的结构体,样例:
|
||||||
|
```json
|
||||||
|
"commands": {
|
||||||
|
"initData": [
|
||||||
|
{
|
||||||
|
"name": "exec",
|
||||||
|
"binPath": "{exePath}/bin/mysqld.exe",
|
||||||
|
"cmds": [
|
||||||
|
"--defaults-file={exePath}/my.ini",
|
||||||
|
"--initialize"
|
||||||
|
],
|
||||||
|
"waiting": 1 //为等待的秒数
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "exec",
|
||||||
|
"binPath": "{exePath}/bin/mysqld.exe",
|
||||||
|
"cmds": [
|
||||||
|
"--defaults-file={exePath}/my.ini",
|
||||||
|
"--init-file={exePath}/password.txt"
|
||||||
|
],
|
||||||
|
"waiting": 3,
|
||||||
|
"kill": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "start"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setting": [
|
||||||
|
{
|
||||||
|
"name": "changeFile",
|
||||||
|
"tplPath": "{exePath}/my.ini.tpl",
|
||||||
|
"filePath": "{exePath}/my.ini"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "initData"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 上述样例中setting又调用了initData命令。
|
||||||
|
- post的固定地址为`http://localhost:56780/store/setting`
|
||||||
|
- 原理是通过`http`请求`/store/setting`接口,将配置信息发送给`store`服务,然后`store`服务会根据配置信息,自动更换配置信息,并启动应用
|
||||||
|
|
||||||
|
### static目录说明
|
||||||
|
1. `index.html`是应用的首页
|
||||||
|
2. `static`目录下的在执行install时文件会自动复制到`.godoos/static/`应用目录下
|
||||||
|
3. `store.json`如果设置了`icon`,并且`static`目录下存在该文件,则应用图标为该文件。否则为`install.json`中的icon
|
||||||
|
|
||||||
### 内置应用说明
|
### 内置应用说明
|
||||||
- 系列封装了一些用于处理进程控制和文件操作的功能函数,以下是各函数的详细描述:
|
- 系统封装了一些用于处理进程控制和文件操作的功能函数,以下是各函数的详细描述:
|
||||||
1. `start` 启动应用
|
1. `start` 启动应用
|
||||||
2. `stop` 停止应用
|
2. `stop` 停止应用
|
||||||
3. `restart` 重启应用
|
3. `restart` 重启应用
|
||||||
4. `exec` 执行命令 必须设置binPath和cmds
|
4. `exec` 执行命令 必须设置binPath和cmds
|
||||||
5. `writeFile` 写入文件 必须设置filePath和content,以config为依据,对content进行替换
|
5. `writeFile` 写入文件 必须设置filePath和content,以config为依据,对content中的`{参数}`进行替换
|
||||||
6. `changeFile` 修改文件 必须设置filePath和tplPath,以config为依据,在模板文件中进行替换
|
6. `changeFile` 修改文件 必须设置filePath和tplPath,以config为依据,在模板文件中进行替换
|
||||||
7. `deleteFile` 删除文件
|
7. `deleteFile` 删除文件
|
||||||
8. `unzip` 解压文件 必须设置filePath和content,content为解压目录
|
8. `unzip` 解压文件 必须设置filePath和content,content为解压目录
|
||||||
9. `zip` 压缩文件 必须设置filePath和content,filePath为将要压缩的文件夹,content为压缩后的文件名
|
9. `zip` 压缩文件 必须设置filePath和content,filePath为将要压缩的文件夹,content为压缩后的文件名
|
||||||
10. `mkdir` 创建文件夹 必须设置FilePath,FilePath为创建的文件夹路径
|
10. `mkdir` 创建文件夹 必须设置FilePath,FilePath为创建的文件夹路径
|
||||||
|
11. `startApp` 启动其他应用,content为应用名
|
||||||
|
12. `stopApp` 停止其他应用,content为应用名
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
13
docs/demo/mysql5.7/install.json
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "mysql5.7",
|
||||||
|
"url": "https://gitee.com/ruitao_admin/godoos-mysql5.7/releases/download/mysql5.7/mysql5.7.zip",
|
||||||
|
"isDev": true,
|
||||||
|
"needDownload": true,
|
||||||
|
"needInstall": true,
|
||||||
|
"version": "5.7.37",
|
||||||
|
"icon": "https://img0.baidu.com/it/u=1087008535,3349487719&fm=253&fmt=auto&app=138&f=PNG?w=969&h=500",
|
||||||
|
"checkProgress": true,
|
||||||
|
"hasRestart": true,
|
||||||
|
"setting": true,
|
||||||
|
"isOn": true
|
||||||
|
}
|
||||||
26
docs/demo/mysql5.7/my.ini
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
[client]
|
||||||
|
port = 3306
|
||||||
|
|
||||||
|
[mysql]
|
||||||
|
no-auto-rehash
|
||||||
|
default-character-set = utf8
|
||||||
|
|
||||||
|
[mysqld]
|
||||||
|
basedir = ""
|
||||||
|
datadir = "data"
|
||||||
|
port = 3306
|
||||||
|
server_id = 1
|
||||||
|
character-set-server = utf8
|
||||||
|
|
||||||
|
explicit_defaults_for_timestamp = on
|
||||||
|
skip-ssl
|
||||||
|
|
||||||
|
table_open_cache = 256
|
||||||
|
|
||||||
|
log_timestamps = SYSTEM
|
||||||
|
|
||||||
|
log-error = "logs/mysql.log"
|
||||||
|
log_syslog = 0
|
||||||
|
[mysqldump]
|
||||||
|
quick
|
||||||
|
max_allowed_packet = 512M
|
||||||
26
docs/demo/mysql5.7/my.ini.tpl
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
[client]
|
||||||
|
port = {port}
|
||||||
|
|
||||||
|
[mysql]
|
||||||
|
no-auto-rehash
|
||||||
|
default-character-set = utf8
|
||||||
|
|
||||||
|
[mysqld]
|
||||||
|
basedir = "{exePath}"
|
||||||
|
datadir = "{dataDir}"
|
||||||
|
port = {port}
|
||||||
|
server_id = 1
|
||||||
|
character-set-server = utf8
|
||||||
|
|
||||||
|
explicit_defaults_for_timestamp = on
|
||||||
|
skip-ssl
|
||||||
|
|
||||||
|
table_open_cache = 256
|
||||||
|
|
||||||
|
log_timestamps = SYSTEM
|
||||||
|
|
||||||
|
log-error = "{logDir}/mysql.log"
|
||||||
|
log_syslog = 0
|
||||||
|
[mysqldump]
|
||||||
|
quick
|
||||||
|
max_allowed_packet = 512M
|
||||||
1
docs/demo/mysql5.7/password.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';
|
||||||
1
docs/demo/mysql5.7/password.txt.tpl
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER USER 'root'@'localhost' IDENTIFIED BY '{password}';
|
||||||
314
docs/demo/mysql5.7/static/index.html
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>MySQL 设置</title>
|
||||||
|
<!-- 可以在这里添加CSS样式 -->
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 10px auto;
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #007BFF;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 模态对话框背景 */
|
||||||
|
.modal {
|
||||||
|
display: none;
|
||||||
|
/* 默认隐藏 */
|
||||||
|
position: fixed;
|
||||||
|
/* 固定位置 */
|
||||||
|
z-index: 1;
|
||||||
|
/* 层叠顺序 */
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
/* 全屏宽度 */
|
||||||
|
height: 100%;
|
||||||
|
/* 全屏高度 */
|
||||||
|
overflow: auto;
|
||||||
|
/* 启用滚动条 */
|
||||||
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
|
/* 半透明黑色背景 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 模态对话框内容 */
|
||||||
|
.modal-content {
|
||||||
|
background-color: #fefefe;
|
||||||
|
margin: 15% auto;
|
||||||
|
/* 15% 从顶部开始,居中 */
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid #888;
|
||||||
|
width: 80%;
|
||||||
|
/* 或者设置为固定宽度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 关闭按钮 */
|
||||||
|
.close {
|
||||||
|
color: #aaa;
|
||||||
|
float: right;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close:hover,
|
||||||
|
.close:focus {
|
||||||
|
color: black;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab按钮样式 */
|
||||||
|
.tab button {
|
||||||
|
background-color: inherit;
|
||||||
|
float: left;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 14px 16px;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 活动的tab按钮高亮 */
|
||||||
|
.tab button:hover {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab button.active {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab页内容样式 */
|
||||||
|
.tabcontent {
|
||||||
|
display: none;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h3>MySQL 设置</h3>
|
||||||
|
<div class="tab">
|
||||||
|
<button class="tablinks" onclick="openTab(event, 'General')">常规设置</button>
|
||||||
|
<button class="tablinks" onclick="openTab(event, 'Password')">设置密码</button>
|
||||||
|
</div>
|
||||||
|
<div id="General" class="tabcontent">
|
||||||
|
<label for="dataDir">存储地址:</label>
|
||||||
|
<input type="text" id="dataDir" name="dataDir">
|
||||||
|
|
||||||
|
<label for="logDir">日志地址:</label>
|
||||||
|
<input type="text" id="logDir" name="logDir">
|
||||||
|
|
||||||
|
<label for="port">端口号:</label>
|
||||||
|
<input type="text" id="port" name="port">
|
||||||
|
|
||||||
|
<button type="submit" id="saveConfig" class="button">保存</button>
|
||||||
|
</div>
|
||||||
|
<div id="Password" class="tabcontent">
|
||||||
|
<label for="password">密码:</label>
|
||||||
|
<input type="password" id="password" name="password" placeholder="请输入数据库密码,不能小于3位">
|
||||||
|
|
||||||
|
<button type="submit" id="savePwd" class="button">保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="myModal" class="modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<span class="close">×</span>
|
||||||
|
<p id="modalText"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function openTab(evt, tabName) {
|
||||||
|
var i, tabcontent, tablinks;
|
||||||
|
tabcontent = document.getElementsByClassName("tabcontent");
|
||||||
|
for (i = 0; i < tabcontent.length; i++) {
|
||||||
|
tabcontent[i].style.display = "none";
|
||||||
|
}
|
||||||
|
tablinks = document.getElementsByClassName("tablinks");
|
||||||
|
for (i = 0; i < tablinks.length; i++) {
|
||||||
|
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
||||||
|
}
|
||||||
|
document.getElementById(tabName).style.display = "flex";
|
||||||
|
evt.currentTarget.className += " active";
|
||||||
|
}
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
let localData = localStorage.getItem('mysqlSettings');
|
||||||
|
if (localData) {
|
||||||
|
localData = JSON.parse(localData);
|
||||||
|
// 填充表单字段
|
||||||
|
document.getElementById('dataDir').value = localData.dataDir;
|
||||||
|
document.getElementById('logDir').value = localData.logDir;
|
||||||
|
document.getElementById('port').value = localData.port;
|
||||||
|
//document.getElementById('password').value = savedData.password;
|
||||||
|
}
|
||||||
|
// 获取模态对话框元素
|
||||||
|
const modal = document.getElementById("myModal");
|
||||||
|
// 获取关闭按钮
|
||||||
|
const span = document.getElementsByClassName("close")[0];
|
||||||
|
// 获取模态对话框文本
|
||||||
|
const modalText = document.getElementById("modalText");
|
||||||
|
// 页面加载时激活第一个 tab
|
||||||
|
document.querySelector('.tablinks:first-child').click();
|
||||||
|
|
||||||
|
|
||||||
|
function showMsg(msg) {
|
||||||
|
// 替换 alert 为显示模态对话框
|
||||||
|
modal.style.display = "block";
|
||||||
|
modalText.textContent = msg;
|
||||||
|
}
|
||||||
|
async function OpenDirDialog() {
|
||||||
|
if (window.go) {
|
||||||
|
return window['go']['app']['App']['OpenDirDialog']();
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.getElementById('dataDir').addEventListener('click', async function () {
|
||||||
|
let dir = await OpenDirDialog();
|
||||||
|
if (dir) {
|
||||||
|
document.getElementById('dataDir').value = dir;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.getElementById('logDir').addEventListener('click', async function () {
|
||||||
|
let dir = await OpenDirDialog();
|
||||||
|
if (dir) {
|
||||||
|
document.getElementById('logDir').value = dir;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 添加事件监听器来处理表单提交
|
||||||
|
document.getElementById('saveConfig').addEventListener('click', async function (event) {
|
||||||
|
event.preventDefault(); // 阻止默认的表单提交行为
|
||||||
|
|
||||||
|
// 获取表单字段的值
|
||||||
|
const dataDir = document.getElementById('dataDir').value;
|
||||||
|
const logDir = document.getElementById('logDir').value;
|
||||||
|
const port = document.getElementById('port').value;
|
||||||
|
|
||||||
|
// 表单字段的校验
|
||||||
|
if (!dataDir || !logDir || !port) {
|
||||||
|
showMsg('所有字段都是必填项!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 端口应该是一个数字
|
||||||
|
if (isNaN(port)) {
|
||||||
|
showMsg('端口号必须是一个数字!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 假设端口号应该在 1 到 65535 之间
|
||||||
|
if (port < 1 || port > 65535) {
|
||||||
|
showMsg('端口号必须在 1 到 65535 之间!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const postData = {
|
||||||
|
dataDir: dataDir,
|
||||||
|
logDir: logDir,
|
||||||
|
port: port,
|
||||||
|
name: "mysql5.7",
|
||||||
|
cmdKey: "setting"
|
||||||
|
}
|
||||||
|
const comp = await fetch('http://localhost:56780/store/setting', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(postData)
|
||||||
|
});
|
||||||
|
if (!comp.ok) {
|
||||||
|
showMsg('设置失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 存储数据到 localStorage
|
||||||
|
localStorage.setItem('mysqlSettings', JSON.stringify(postData));
|
||||||
|
showMsg('设置成功');
|
||||||
|
});
|
||||||
|
document.getElementById('savePwd').addEventListener('click', async function (event) {
|
||||||
|
event.preventDefault(); // 阻止默认的表单提交行为
|
||||||
|
|
||||||
|
// 获取表单字段的值
|
||||||
|
const password = document.getElementById('password').value;
|
||||||
|
|
||||||
|
// 表单字段的校验
|
||||||
|
if (!password || password.length < 3) {
|
||||||
|
showMsg('密码不能为空');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pwData = {
|
||||||
|
password: password,
|
||||||
|
name: "mysql5.7",
|
||||||
|
cmdKey: "changePassword"
|
||||||
|
}
|
||||||
|
const res = await fetch('http://localhost:56780/store/setting', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(pwData)
|
||||||
|
});
|
||||||
|
if (!res.ok) {
|
||||||
|
showMsg('设置失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showMsg('设置成功');
|
||||||
|
});
|
||||||
|
// 当用户点击关闭按钮时关闭模态对话框
|
||||||
|
span.onclick = function () {
|
||||||
|
modal.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当用户点击模态对话框以外的地方时关闭模态对话框
|
||||||
|
window.onclick = function (event) {
|
||||||
|
if (event.target == modal) {
|
||||||
|
modal.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
BIN
docs/demo/mysql5.7/static/mysql.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
84
docs/demo/mysql5.7/store.json
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
{
|
||||||
|
"name": "mysql5.7",
|
||||||
|
"icon": "mysql.png",
|
||||||
|
"setting": {
|
||||||
|
"binPath": "{exePath}/bin/mysqld.exe",
|
||||||
|
"confPath": "{exePath}/my.ini",
|
||||||
|
"progressName": "mysqld.exe",
|
||||||
|
"isOn": true
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"password": "root",
|
||||||
|
"port": 3306,
|
||||||
|
"dataDir": "{exePath}/data",
|
||||||
|
"logDir": "{exePath}/logs"
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"initData": [
|
||||||
|
{
|
||||||
|
"name": "exec",
|
||||||
|
"binPath": "{exePath}/bin/mysqld.exe",
|
||||||
|
"cmds": [
|
||||||
|
"--defaults-file={exePath}/my.ini",
|
||||||
|
"--initialize"
|
||||||
|
],
|
||||||
|
"waiting": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "exec",
|
||||||
|
"binPath": "{exePath}/bin/mysqld.exe",
|
||||||
|
"cmds": [
|
||||||
|
"--defaults-file={exePath}/my.ini",
|
||||||
|
"--init-file={exePath}/password.txt"
|
||||||
|
],
|
||||||
|
"waiting": 3,
|
||||||
|
"kill": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "start"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setting": [
|
||||||
|
{
|
||||||
|
"name": "changeFile",
|
||||||
|
"tplPath": "{exePath}/my.ini.tpl",
|
||||||
|
"filePath": "{exePath}/my.ini"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "initData"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"changePassword": [
|
||||||
|
{
|
||||||
|
"name": "changeFile",
|
||||||
|
"tplPath": "{exePath}/password.txt.tpl",
|
||||||
|
"filePath": "{exePath}/password.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stop"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "exec",
|
||||||
|
"binPath": "{exePath}/bin/mysqld.exe",
|
||||||
|
"cmds": [
|
||||||
|
"--defaults-file={exePath}/my.ini",
|
||||||
|
"--init-file={exePath}/password.txt"
|
||||||
|
],
|
||||||
|
"waiting": 3,
|
||||||
|
"kill": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"install": {
|
||||||
|
"installEnvs": [],
|
||||||
|
"installCmds": [
|
||||||
|
"initData"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"startEnvs": [],
|
||||||
|
"startCmds": [
|
||||||
|
"--defaults-file={exePath}/my.ini"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 91 KiB |
BIN
docs/img/cal.png
|
Before Width: | Height: | Size: 526 KiB |
BIN
docs/img/doc.png
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 456 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 211 KiB |
|
Before Width: | Height: | Size: 513 KiB |
BIN
docs/img/ie.png
|
Before Width: | Height: | Size: 543 KiB |
|
Before Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 426 KiB |
|
Before Width: | Height: | Size: 441 KiB |
|
Before Width: | Height: | Size: 93 KiB |
BIN
docs/img/pic.png
|
Before Width: | Height: | Size: 147 KiB |
BIN
docs/img/ppt.png
|
Before Width: | Height: | Size: 272 KiB |
|
Before Width: | Height: | Size: 600 KiB |
|
Before Width: | Height: | Size: 430 KiB |
|
|
@ -15,9 +15,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="upgrade-content">
|
<div class="upgrade-content">
|
||||||
GodoOS {{ $t('upgrade.msg') }}
|
GodoOS {{ upgradeStore.upgradeDesc }}
|
||||||
<div class="mt5">
|
<div class="mt5">
|
||||||
<el-link type="primary" class="font12" href="https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md" target="_black">
|
<el-link type="primary" class="font12" href="https://gitee.com/ruitao_admin/godoos/blob/master/CHANGELOG.md" target="_black">
|
||||||
CHANGELOG.md
|
CHANGELOG.md
|
||||||
</el-link>
|
</el-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,14 @@ import { ref } from "vue";
|
||||||
import { getSystemKey, setSystemKey, parseJson, getSystemConfig } from '@/system/config'
|
import { getSystemKey, setSystemKey, parseJson, getSystemConfig } from '@/system/config'
|
||||||
import { RestartApp } from '@/util/goutil';
|
import { RestartApp } from '@/util/goutil';
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { t } from '@/i18n';
|
||||||
export const useUpgradeStore = defineStore('upgradeStore', () => {
|
export const useUpgradeStore = defineStore('upgradeStore', () => {
|
||||||
const hasUpgrade = ref(false);
|
const hasUpgrade = ref(false);
|
||||||
const hasNotice = ref(false);
|
const hasNotice = ref(false);
|
||||||
const hasAd = ref(false);
|
const hasAd = ref(false);
|
||||||
const updateUrl = ref('');
|
const updateUrl = ref('');
|
||||||
const versionTag = ref(0)
|
const versionTag = ref(0)
|
||||||
|
const upgradeDesc = ref('')
|
||||||
const currentVersion = ref(0)
|
const currentVersion = ref(0)
|
||||||
const progress = ref(0)
|
const progress = ref(0)
|
||||||
const noticeList:any = ref([])
|
const noticeList:any = ref([])
|
||||||
|
|
@ -21,6 +23,7 @@ export const useUpgradeStore = defineStore('upgradeStore', () => {
|
||||||
const releaseData = await releaseRes.json()
|
const releaseData = await releaseRes.json()
|
||||||
versionTag.value = releaseData.version
|
versionTag.value = releaseData.version
|
||||||
if(versionTag.value > config.version){
|
if(versionTag.value > config.version){
|
||||||
|
upgradeDesc.value = releaseData.desc ?? t('upgrade.msg')
|
||||||
hasUpgrade.value = true
|
hasUpgrade.value = true
|
||||||
updateUrl.value = releaseData.url
|
updateUrl.value = releaseData.url
|
||||||
}
|
}
|
||||||
|
|
@ -78,6 +81,7 @@ export const useUpgradeStore = defineStore('upgradeStore', () => {
|
||||||
hasNotice,
|
hasNotice,
|
||||||
hasAd,
|
hasAd,
|
||||||
versionTag,
|
versionTag,
|
||||||
|
upgradeDesc,
|
||||||
updateUrl,
|
updateUrl,
|
||||||
noticeList,
|
noticeList,
|
||||||
adList,
|
adList,
|
||||||
|
|
|
||||||
|
|
@ -15,19 +15,21 @@ import (
|
||||||
func runStop(storeInfo StoreInfo) error {
|
func runStop(storeInfo StoreInfo) error {
|
||||||
return StopCmd(storeInfo.Name)
|
return StopCmd(storeInfo.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runStart(storeInfo StoreInfo) error {
|
func runStart(storeInfo StoreInfo) error {
|
||||||
err := SetEnvs(storeInfo.Start.Envs)
|
err := SetEnvs(storeInfo.Start.StartEnvs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to set start environment variable %s: %w", storeInfo.Name, err)
|
return fmt.Errorf("failed to set start environment variable %s: %w", storeInfo.Name, err)
|
||||||
}
|
}
|
||||||
if !libs.PathExists(storeInfo.Setting.BinPath) {
|
binPath := storeInfo.Setting.BinPath
|
||||||
|
if !libs.PathExists(binPath) {
|
||||||
return fmt.Errorf("script file %s does not exist", storeInfo.Setting.BinPath)
|
return fmt.Errorf("script file %s does not exist", storeInfo.Setting.BinPath)
|
||||||
}
|
}
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
if len(storeInfo.Start.Cmds) > 0 {
|
if len(storeInfo.Start.StartCmds) > 0 {
|
||||||
cmd = exec.Command(storeInfo.Setting.BinPath, storeInfo.Start.Cmds...)
|
cmd = exec.Command(binPath, storeInfo.Start.StartCmds...)
|
||||||
} else {
|
} else {
|
||||||
cmd = exec.Command(storeInfo.Setting.BinPath)
|
cmd = exec.Command(binPath)
|
||||||
}
|
}
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
// 在Windows上,通过设置CreationFlags来隐藏窗口
|
// 在Windows上,通过设置CreationFlags来隐藏窗口
|
||||||
|
|
@ -50,6 +52,12 @@ func runStart(storeInfo StoreInfo) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func RunStartApp(appName string) error {
|
||||||
|
return ExecuteScript(appName)
|
||||||
|
}
|
||||||
|
func RunStopApp(appName string) error {
|
||||||
|
return StopCmd(appName)
|
||||||
|
}
|
||||||
func runRestart(storeInfo StoreInfo) error {
|
func runRestart(storeInfo StoreInfo) error {
|
||||||
err := runStop(storeInfo)
|
err := runStop(storeInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -192,13 +192,13 @@ func convertValueToString(value any) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func InstallStore(storeInfo StoreInfo) error {
|
func InstallStore(storeInfo StoreInfo) error {
|
||||||
err := SetEnvs(storeInfo.Install.Envs)
|
err := SetEnvs(storeInfo.Install.InstallEnvs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to set install environment variable %s: %w", storeInfo.Name, err)
|
return fmt.Errorf("failed to set install environment variable %s: %w", storeInfo.Name, err)
|
||||||
}
|
}
|
||||||
if len(storeInfo.Install.Cmds) > 0 {
|
if len(storeInfo.Install.InstallCmds) > 0 {
|
||||||
for _, cmdKey := range storeInfo.Install.Cmds {
|
for _, cmdKey := range storeInfo.Install.InstallCmds {
|
||||||
if _, ok := storeInfo.Cmds[cmdKey]; ok {
|
if _, ok := storeInfo.Commands[cmdKey]; ok {
|
||||||
// 如果命令存在,你可以进一步处理 cmds
|
// 如果命令存在,你可以进一步处理 cmds
|
||||||
RunCmds(storeInfo, cmdKey)
|
RunCmds(storeInfo, cmdKey)
|
||||||
}
|
}
|
||||||
|
|
@ -214,6 +214,12 @@ func RunCmd(storeInfo StoreInfo, cmd Cmd) error {
|
||||||
if cmd.Name == "start" {
|
if cmd.Name == "start" {
|
||||||
runStart(storeInfo)
|
runStart(storeInfo)
|
||||||
}
|
}
|
||||||
|
if cmd.Name == "startApp" {
|
||||||
|
RunStartApp(cmd.Content)
|
||||||
|
}
|
||||||
|
if cmd.Name == "stopApp" {
|
||||||
|
RunStopApp(cmd.Content)
|
||||||
|
}
|
||||||
if cmd.Name == "restart" {
|
if cmd.Name == "restart" {
|
||||||
runRestart(storeInfo)
|
runRestart(storeInfo)
|
||||||
}
|
}
|
||||||
|
|
@ -259,9 +265,9 @@ func RunCmd(storeInfo StoreInfo, cmd Cmd) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func RunCmds(storeInfo StoreInfo, cmdKey string) error {
|
func RunCmds(storeInfo StoreInfo, cmdKey string) error {
|
||||||
cmds := storeInfo.Cmds[cmdKey]
|
cmds := storeInfo.Commands[cmdKey]
|
||||||
for _, cmd := range cmds {
|
for _, cmd := range cmds {
|
||||||
if _, ok := storeInfo.Cmds[cmd.Name]; ok {
|
if _, ok := storeInfo.Commands[cmd.Name]; ok {
|
||||||
RunCmds(storeInfo, cmd.Name)
|
RunCmds(storeInfo, cmd.Name)
|
||||||
} else {
|
} else {
|
||||||
RunCmd(storeInfo, cmd)
|
RunCmd(storeInfo, cmd)
|
||||||
|
|
@ -333,12 +339,13 @@ func replacePlaceholdersInCmds(storeInfo *StoreInfo) {
|
||||||
// 遍历Config中的所有键值对
|
// 遍历Config中的所有键值对
|
||||||
for key, value := range storeInfo.Config {
|
for key, value := range storeInfo.Config {
|
||||||
// 遍历Cmds中的所有命令组
|
// 遍历Cmds中的所有命令组
|
||||||
for cmdGroupName, cmds := range storeInfo.Cmds {
|
for cmdGroupName, cmds := range storeInfo.Commands {
|
||||||
// 遍历每个命令组中的所有Cmd
|
// 遍历每个命令组中的所有Cmd
|
||||||
for i, cmd := range cmds {
|
for i, cmd := range cmds {
|
||||||
|
if cmd.Content != "" {
|
||||||
strValue := convertValueToString(value)
|
strValue := convertValueToString(value)
|
||||||
// 使用正则表达式替换Content中的占位符
|
// 使用正则表达式替换Content中的占位符
|
||||||
storeInfo.Cmds[cmdGroupName][i].Content = pattern.ReplaceAllStringFunc(
|
storeInfo.Commands[cmdGroupName][i].Content = pattern.ReplaceAllStringFunc(
|
||||||
cmd.Content,
|
cmd.Content,
|
||||||
func(match string) string {
|
func(match string) string {
|
||||||
// 检查占位符是否与Config中的键匹配
|
// 检查占位符是否与Config中的键匹配
|
||||||
|
|
@ -349,6 +356,8 @@ func replacePlaceholdersInCmds(storeInfo *StoreInfo) {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,12 +89,13 @@ func StoreSettingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
storeInfo.Config[k] = v // 如果k存在,则更新;如果不存在,则新增
|
storeInfo.Config[k] = v // 如果k存在,则更新;如果不存在,则新增
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
replacePlaceholdersInCmds(&storeInfo)
|
||||||
err = SaveInfoFile(storeInfo)
|
err = SaveInfoFile(storeInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
libs.ErrorMsg(w, "the store info.json is error: "+err.Error())
|
libs.ErrorMsg(w, "the store info.json is error: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, ok := storeInfo.Cmds[cmdKey]
|
_, ok := storeInfo.Commands[cmdKey]
|
||||||
if !ok {
|
if !ok {
|
||||||
libs.ErrorMsg(w, "cmdKey is not found")
|
libs.ErrorMsg(w, "cmdKey is not found")
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,9 @@ type StoreInfo struct {
|
||||||
Icon string `json:"icon"` // 应用程序商店的图标路径。
|
Icon string `json:"icon"` // 应用程序商店的图标路径。
|
||||||
Setting Setting `json:"setting"` // 应用程序商店的配置信息。
|
Setting Setting `json:"setting"` // 应用程序商店的配置信息。
|
||||||
Config map[string]any `json:"config"` // 应用程序的配置信息映射。
|
Config map[string]any `json:"config"` // 应用程序的配置信息映射。
|
||||||
Cmds map[string][]Cmd `json:"cmds"` // 应用程序的命令集合。
|
Commands map[string][]Cmd `json:"commands"` // 应用程序的命令集合。
|
||||||
Install Install `json:"install"` // 安装应用程序的信息。
|
Install InstallStruct `json:"install"` // 安装应用程序的信息。
|
||||||
Start Install `json:"start"` // 启动应用程序的信息。
|
Start StartStruct `json:"start"` // 启动应用程序的信息。
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting 描述了应用程序的设置信息。
|
// Setting 描述了应用程序的设置信息。
|
||||||
|
|
@ -60,7 +60,11 @@ type Cmd struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install 描述了安装过程中的环境变量和命令列表。
|
// Install 描述了安装过程中的环境变量和命令列表。
|
||||||
type Install struct {
|
type InstallStruct struct {
|
||||||
Envs []Item `json:"envs"` // 安装过程中需要的环境变量。
|
InstallEnvs []Item `json:"installEnvs"` // 安装过程中需要的环境变量。
|
||||||
Cmds []string `json:"cmds"` // 安装过程中需要执行的命令列表。
|
InstallCmds []string `json:"installCmds"` // 安装过程中需要执行的命令列表。
|
||||||
|
}
|
||||||
|
type StartStruct struct {
|
||||||
|
StartEnvs []Item `json:"startEnvs"` // 安装过程中需要的环境变量。
|
||||||
|
StartCmds []string `json:"startCmds"` // 安装过程中需要执行的命令列表。
|
||||||
}
|
}
|
||||||
|
|
|
||||||