add demo
|
|
@ -45,20 +45,20 @@
|
|||
- ***无需联网使用,全开源***
|
||||
- ***零配置,无需注册,下载即用***
|
||||
- ***零污染,无插件依赖***
|
||||
- ***精小,打包后仅65M,确包含了所有的办公套件***
|
||||
- ***精小,打包后仅65M,却包含了所有的办公套件***
|
||||
- ***可无限扩展,支持自定义应用***
|
||||
- ***golang开发后端,低资源消耗和高性能***
|
||||
- ***支持多平台,Windows、Linux、MacOS***
|
||||
- ***只会html和拥有可执行的应用,简单学习一下应用商店配置即可开发出复杂的应用***
|
||||
- ***完善的应用商店体系,简单学习一下[应用商店配置](./docs/Store.md)即可开发出复杂的应用***
|
||||
|
||||
## 🚧 开发进程
|
||||
- 2024年7月,发布v1.0.0版本,发布后,项目进入第二阶段。
|
||||
- 2024年8月1日,发布v1.0.0版本,发布后,项目进入第二阶段。
|
||||
|
||||
## 🏭 第二阶段目标
|
||||
- [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`两个文件。 配置文件格式如下:
|
||||
- install.json
|
||||
- install.json [样本](./demo/mysql5.7/install.json)
|
||||
```json
|
||||
{
|
||||
"name": "", // string, 应用程序名称。
|
||||
"url": "", // string, 应用程序下载地址。
|
||||
"webUrl":"", // string, 如何设置,应用程序将显示到桌面。
|
||||
"isDev": true, // boolean, 是否为开发环境。
|
||||
"needDownload": true, // boolean, 是否需要下载。
|
||||
"needDownload": true, // boolean, 是否需要下载,本地开发设置为false。
|
||||
"needInstall": true, // boolean, 是否需要安装。
|
||||
"version": "1.0.0", // string, 应用程序版本。
|
||||
"icon": "", // string, 应用程序图标,为可访问的网络地址。
|
||||
|
|
@ -21,9 +31,9 @@
|
|||
}
|
||||
|
||||
```
|
||||
如果不需要后端进程的web应用,store.json可以不用配置。
|
||||
- 备注:如果不需要后端进程的web应用,store.json可以不用配置。
|
||||
|
||||
install.json的struct为:
|
||||
install.json的结构体为:
|
||||
```json
|
||||
type InstallInfo struct {
|
||||
Name string `json:"name"` // 应用程序名称。重要,必须和应用的目录名称一致。
|
||||
|
|
@ -42,7 +52,7 @@ type InstallInfo struct {
|
|||
}
|
||||
```
|
||||
|
||||
- store.json
|
||||
- store.json [样本](./demo/mysql5.7/store.json)
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -50,33 +60,38 @@ type InstallInfo struct {
|
|||
"icon": "mysql.png", // string, 应用程序图标,一般放在应用程序static目录下。
|
||||
"setting": {
|
||||
"binPath": "{exePath}/bin/mysqld.exe", // string, 重要,必须设置。为启动程序路径。
|
||||
"confPath": "{exePath}/my.ini",// string, 重要,必须设置。为配置文件路径。
|
||||
"confPath": "{exePath}/my.ini",// string, 可为空。为配置文件路径。
|
||||
"progressName": "mysqld.exe",// string, 进程名称。如果为单线程可不设置。
|
||||
"isOn": true // boolean, 是否启动守护进程
|
||||
},
|
||||
"config": { // object, 配置文件。里面的配置可以任意填写,和cmds配合使用。
|
||||
"config": { // object, 配置文件。里面的配置可以任意填写,和commands配合使用。可以通过http设置里面的参数。
|
||||
},
|
||||
"cmds": {},// object, 命令集。
|
||||
"commands": {},// object, 命令列表集。可供install里的installCmds调用,也可以通过外部http请求调用。
|
||||
"install": { // object, 安装配置。
|
||||
"envs": [],// object[], 环境变量。
|
||||
"cmds": []// object[], 启动命令。
|
||||
"installEnvs": [],// object[], 环境变量。
|
||||
"installCmds": []// object[], 启动命令。可调用命令列表集commands里面的命令。
|
||||
},
|
||||
"start": {// object, 启动配置。
|
||||
"envs": [],// object[], 环境变量。
|
||||
"cmds": []// object[], 启动命令。
|
||||
"start": {
|
||||
"startEnvs": [],
|
||||
"startCmds": [// object[], 纯参数命令集。将启动`setting.binPath`,不可调用命令列表集`commands`里面的命令。
|
||||
"--defaults-file={exePath}/my.ini"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 备注:核心的替换参数为`{exePath}`即程序的执行目录。其他`{参数}`对应`store.json`里的`config`。
|
||||
|
||||
store.json的struct为
|
||||
```json
|
||||
type StoreInfo struct {
|
||||
Name string `json:"name"` // 应用程序的名称。
|
||||
Icon string `json:"icon"` // 应用程序的图标路径。
|
||||
Setting Setting `json:"setting"` // 应用程序的配置信息。
|
||||
Name string `json:"name"` // 应用程序商店的名称。
|
||||
Icon string `json:"icon"` // 应用程序商店的图标路径。
|
||||
Setting Setting `json:"setting"` // 应用程序商店的配置信息。
|
||||
Config map[string]any `json:"config"` // 应用程序的配置信息映射。
|
||||
Cmds map[string][]Cmd `json:"cmds"` // 应用程序的命令集合。
|
||||
Install Install `json:"install"` // 安装应用程序的信息。
|
||||
Start Install `json:"start"` // 启动应用程序的信息。
|
||||
Commands map[string][]Cmd `json:"commands"` // 应用程序的命令集合。
|
||||
Install InstallStruct `json:"install"` // 安装应用程序的信息。
|
||||
Start StartStruct `json:"start"` // 启动应用程序的信息。
|
||||
}
|
||||
```
|
||||
Setting的结构体为:
|
||||
|
|
@ -103,12 +118,16 @@ type Cmd struct {
|
|||
Envs []Item `json:"envs"` // 命令执行时的环境变量。
|
||||
}
|
||||
```
|
||||
Install的struct为:
|
||||
Install的结构体为:
|
||||
```json
|
||||
// Install 描述了安装过程中的环境变量和命令列表。
|
||||
type Install struct {
|
||||
Envs []Item `json:"envs"` // 安装过程中需要的环境变量。
|
||||
Cmds []string `json:"cmds"` // 安装过程中需要执行的命令列表。
|
||||
// InstallStruct 描述了安装过程中的环境变量和命令列表。
|
||||
type InstallStruct struct {
|
||||
InstallEnvs []Item `json:"installEnvs"` // 安装过程中需要的环境变量。
|
||||
InstallCmds []string `json:"installCmds"` // 安装过程中需要执行的命令列表。
|
||||
}
|
||||
type StartStruct struct {
|
||||
StartEnvs []Item `json:"startEnvs"` // 安装过程中需要的环境变量。
|
||||
StartCmds []string `json:"startCmds"` // 安装过程中需要执行的命令列表。
|
||||
}
|
||||
// Item 是一个通用的键值对结构体,用于表示配置项或环境变量等。
|
||||
type Item struct {
|
||||
|
|
@ -121,13 +140,13 @@ type Item struct {
|
|||
|
||||
### 配置文件`store.json`说明
|
||||
|
||||
1. `install`和`start`配置可以调用`cmds`里的命令。
|
||||
2. `cmds`里的命令也可以调用自身的命令。
|
||||
1. `install`可以调用`commands`里的命令。
|
||||
2. `commands`里的命令也可以调用自身的命令。
|
||||
3. 所有的命令都可以串联使用。
|
||||
|
||||
### 如何设置配置
|
||||
1. 在应用目录下建立`static`目录,并创建`index.html`文件。`install.json`中`setting`设置为`true`。
|
||||
前端配置样列
|
||||
前端配置样列 [样本](./demo/mysql5.7/static/index.html)
|
||||
```js
|
||||
const postData = {
|
||||
dataDir: dataDir,// 此处对应store.json中的config配置项
|
||||
|
|
@ -144,17 +163,67 @@ const comp = await fetch('http://localhost:56780/store/setting', {
|
|||
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` 启动应用
|
||||
2. `stop` 停止应用
|
||||
3. `restart` 重启应用
|
||||
4. `exec` 执行命令 必须设置binPath和cmds
|
||||
5. `writeFile` 写入文件 必须设置filePath和content,以config为依据,对content进行替换
|
||||
5. `writeFile` 写入文件 必须设置filePath和content,以config为依据,对content中的`{参数}`进行替换
|
||||
6. `changeFile` 修改文件 必须设置filePath和tplPath,以config为依据,在模板文件中进行替换
|
||||
7. `deleteFile` 删除文件
|
||||
8. `unzip` 解压文件 必须设置filePath和content,content为解压目录
|
||||
9. `zip` 压缩文件 必须设置filePath和content,filePath为将要压缩的文件夹,content为压缩后的文件名
|
||||
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 class="upgrade-content">
|
||||
GodoOS {{ $t('upgrade.msg') }}
|
||||
GodoOS {{ upgradeStore.upgradeDesc }}
|
||||
<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
|
||||
</el-link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@ import { ref } from "vue";
|
|||
import { getSystemKey, setSystemKey, parseJson, getSystemConfig } from '@/system/config'
|
||||
import { RestartApp } from '@/util/goutil';
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { t } from '@/i18n';
|
||||
export const useUpgradeStore = defineStore('upgradeStore', () => {
|
||||
const hasUpgrade = ref(false);
|
||||
const hasNotice = ref(false);
|
||||
const hasAd = ref(false);
|
||||
const updateUrl = ref('');
|
||||
const versionTag = ref(0)
|
||||
const upgradeDesc = ref('')
|
||||
const currentVersion = ref(0)
|
||||
const progress = ref(0)
|
||||
const noticeList:any = ref([])
|
||||
|
|
@ -21,6 +23,7 @@ export const useUpgradeStore = defineStore('upgradeStore', () => {
|
|||
const releaseData = await releaseRes.json()
|
||||
versionTag.value = releaseData.version
|
||||
if(versionTag.value > config.version){
|
||||
upgradeDesc.value = releaseData.desc ?? t('upgrade.msg')
|
||||
hasUpgrade.value = true
|
||||
updateUrl.value = releaseData.url
|
||||
}
|
||||
|
|
@ -78,6 +81,7 @@ export const useUpgradeStore = defineStore('upgradeStore', () => {
|
|||
hasNotice,
|
||||
hasAd,
|
||||
versionTag,
|
||||
upgradeDesc,
|
||||
updateUrl,
|
||||
noticeList,
|
||||
adList,
|
||||
|
|
|
|||
|
|
@ -15,19 +15,21 @@ import (
|
|||
func runStop(storeInfo StoreInfo) error {
|
||||
return StopCmd(storeInfo.Name)
|
||||
}
|
||||
|
||||
func runStart(storeInfo StoreInfo) error {
|
||||
err := SetEnvs(storeInfo.Start.Envs)
|
||||
err := SetEnvs(storeInfo.Start.StartEnvs)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
var cmd *exec.Cmd
|
||||
if len(storeInfo.Start.Cmds) > 0 {
|
||||
cmd = exec.Command(storeInfo.Setting.BinPath, storeInfo.Start.Cmds...)
|
||||
if len(storeInfo.Start.StartCmds) > 0 {
|
||||
cmd = exec.Command(binPath, storeInfo.Start.StartCmds...)
|
||||
} else {
|
||||
cmd = exec.Command(storeInfo.Setting.BinPath)
|
||||
cmd = exec.Command(binPath)
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
// 在Windows上,通过设置CreationFlags来隐藏窗口
|
||||
|
|
@ -50,6 +52,12 @@ func runStart(storeInfo StoreInfo) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
func RunStartApp(appName string) error {
|
||||
return ExecuteScript(appName)
|
||||
}
|
||||
func RunStopApp(appName string) error {
|
||||
return StopCmd(appName)
|
||||
}
|
||||
func runRestart(storeInfo StoreInfo) error {
|
||||
err := runStop(storeInfo)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -192,13 +192,13 @@ func convertValueToString(value any) string {
|
|||
}
|
||||
|
||||
func InstallStore(storeInfo StoreInfo) error {
|
||||
err := SetEnvs(storeInfo.Install.Envs)
|
||||
err := SetEnvs(storeInfo.Install.InstallEnvs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set install environment variable %s: %w", storeInfo.Name, err)
|
||||
}
|
||||
if len(storeInfo.Install.Cmds) > 0 {
|
||||
for _, cmdKey := range storeInfo.Install.Cmds {
|
||||
if _, ok := storeInfo.Cmds[cmdKey]; ok {
|
||||
if len(storeInfo.Install.InstallCmds) > 0 {
|
||||
for _, cmdKey := range storeInfo.Install.InstallCmds {
|
||||
if _, ok := storeInfo.Commands[cmdKey]; ok {
|
||||
// 如果命令存在,你可以进一步处理 cmds
|
||||
RunCmds(storeInfo, cmdKey)
|
||||
}
|
||||
|
|
@ -214,6 +214,12 @@ func RunCmd(storeInfo StoreInfo, cmd Cmd) error {
|
|||
if cmd.Name == "start" {
|
||||
runStart(storeInfo)
|
||||
}
|
||||
if cmd.Name == "startApp" {
|
||||
RunStartApp(cmd.Content)
|
||||
}
|
||||
if cmd.Name == "stopApp" {
|
||||
RunStopApp(cmd.Content)
|
||||
}
|
||||
if cmd.Name == "restart" {
|
||||
runRestart(storeInfo)
|
||||
}
|
||||
|
|
@ -259,9 +265,9 @@ func RunCmd(storeInfo StoreInfo, cmd Cmd) error {
|
|||
return nil
|
||||
}
|
||||
func RunCmds(storeInfo StoreInfo, cmdKey string) error {
|
||||
cmds := storeInfo.Cmds[cmdKey]
|
||||
cmds := storeInfo.Commands[cmdKey]
|
||||
for _, cmd := range cmds {
|
||||
if _, ok := storeInfo.Cmds[cmd.Name]; ok {
|
||||
if _, ok := storeInfo.Commands[cmd.Name]; ok {
|
||||
RunCmds(storeInfo, cmd.Name)
|
||||
} else {
|
||||
RunCmd(storeInfo, cmd)
|
||||
|
|
@ -333,12 +339,13 @@ func replacePlaceholdersInCmds(storeInfo *StoreInfo) {
|
|||
// 遍历Config中的所有键值对
|
||||
for key, value := range storeInfo.Config {
|
||||
// 遍历Cmds中的所有命令组
|
||||
for cmdGroupName, cmds := range storeInfo.Cmds {
|
||||
for cmdGroupName, cmds := range storeInfo.Commands {
|
||||
// 遍历每个命令组中的所有Cmd
|
||||
for i, cmd := range cmds {
|
||||
if cmd.Content != "" {
|
||||
strValue := convertValueToString(value)
|
||||
// 使用正则表达式替换Content中的占位符
|
||||
storeInfo.Cmds[cmdGroupName][i].Content = pattern.ReplaceAllStringFunc(
|
||||
storeInfo.Commands[cmdGroupName][i].Content = pattern.ReplaceAllStringFunc(
|
||||
cmd.Content,
|
||||
func(match string) string {
|
||||
// 检查占位符是否与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存在,则更新;如果不存在,则新增
|
||||
}
|
||||
}
|
||||
replacePlaceholdersInCmds(&storeInfo)
|
||||
err = SaveInfoFile(storeInfo)
|
||||
if err != nil {
|
||||
libs.ErrorMsg(w, "the store info.json is error: "+err.Error())
|
||||
return
|
||||
}
|
||||
_, ok := storeInfo.Cmds[cmdKey]
|
||||
_, ok := storeInfo.Commands[cmdKey]
|
||||
if !ok {
|
||||
libs.ErrorMsg(w, "cmdKey is not found")
|
||||
return
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ type StoreInfo struct {
|
|||
Icon string `json:"icon"` // 应用程序商店的图标路径。
|
||||
Setting Setting `json:"setting"` // 应用程序商店的配置信息。
|
||||
Config map[string]any `json:"config"` // 应用程序的配置信息映射。
|
||||
Cmds map[string][]Cmd `json:"cmds"` // 应用程序的命令集合。
|
||||
Install Install `json:"install"` // 安装应用程序的信息。
|
||||
Start Install `json:"start"` // 启动应用程序的信息。
|
||||
Commands map[string][]Cmd `json:"commands"` // 应用程序的命令集合。
|
||||
Install InstallStruct `json:"install"` // 安装应用程序的信息。
|
||||
Start StartStruct `json:"start"` // 启动应用程序的信息。
|
||||
}
|
||||
|
||||
// Setting 描述了应用程序的设置信息。
|
||||
|
|
@ -60,7 +60,11 @@ type Cmd struct {
|
|||
}
|
||||
|
||||
// Install 描述了安装过程中的环境变量和命令列表。
|
||||
type Install struct {
|
||||
Envs []Item `json:"envs"` // 安装过程中需要的环境变量。
|
||||
Cmds []string `json:"cmds"` // 安装过程中需要执行的命令列表。
|
||||
type InstallStruct struct {
|
||||
InstallEnvs []Item `json:"installEnvs"` // 安装过程中需要的环境变量。
|
||||
InstallCmds []string `json:"installCmds"` // 安装过程中需要执行的命令列表。
|
||||
}
|
||||
type StartStruct struct {
|
||||
StartEnvs []Item `json:"startEnvs"` // 安装过程中需要的环境变量。
|
||||
StartCmds []string `json:"startCmds"` // 安装过程中需要执行的命令列表。
|
||||
}
|
||||
|
|
|
|||