Browse Source

add demo

master
godo 10 months ago
parent
commit
a92cc1f291
  1. 8
      README.md
  2. 135
      docs/Store.md
  3. 13
      docs/demo/mysql5.7/install.json
  4. 26
      docs/demo/mysql5.7/my.ini
  5. 26
      docs/demo/mysql5.7/my.ini.tpl
  6. 1
      docs/demo/mysql5.7/password.txt
  7. 1
      docs/demo/mysql5.7/password.txt.tpl
  8. 314
      docs/demo/mysql5.7/static/index.html
  9. BIN
      docs/demo/mysql5.7/static/mysql.png
  10. 84
      docs/demo/mysql5.7/store.json
  11. BIN
      docs/img/baiban.png
  12. BIN
      docs/img/cal.png
  13. BIN
      docs/img/doc.png
  14. BIN
      docs/img/excel.png
  15. BIN
      docs/img/file.png
  16. BIN
      docs/img/fileeditor.png
  17. BIN
      docs/img/gant.png
  18. BIN
      docs/img/home.png
  19. BIN
      docs/img/ie.png
  20. BIN
      docs/img/kanban.png
  21. BIN
      docs/img/localchat.png
  22. BIN
      docs/img/markdown.png
  23. BIN
      docs/img/mind.png
  24. BIN
      docs/img/pic.png
  25. BIN
      docs/img/ppt.png
  26. BIN
      docs/img/rili.png
  27. BIN
      docs/img/setting-store.png
  28. 4
      frontend/src/components/desktop/Upgrade.vue
  29. 4
      frontend/src/stores/upgrade.ts
  30. 18
      godo/store/cmds.go
  31. 47
      godo/store/install.go
  32. 3
      godo/store/store.go
  33. 24
      godo/store/types.go

8
README.md

@ -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] 开发更多的商店应用
## ⚡ 功能说明和预览

135
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"` // 应用程序的配置信息。
Config map[string]any `json:"config"` // 应用程序的配置信息映射。
Cmds map[string][]Cmd `json:"cmds"` // 应用程序的命令集合。
Install Install `json:"install"` // 安装应用程序的信息。
Start Install `json:"start"` // 启动应用程序的信息。
Name string `json:"name"` // 应用程序商店的名称。
Icon string `json:"icon"` // 应用程序商店的图标路径。
Setting Setting `json:"setting"` // 应用程序商店的配置信息。
Config map[string]any `json:"config"` // 应用程序的配置信息映射。
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

@ -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

@ -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

@ -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

@ -0,0 +1 @@
ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';

1
docs/demo/mysql5.7/password.txt.tpl

@ -0,0 +1 @@
ALTER USER 'root'@'localhost' IDENTIFIED BY '{password}';

314
docs/demo/mysql5.7/static/index.html

@ -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">&times;</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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

84
docs/demo/mysql5.7/store.json

@ -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"
]
}
}

BIN
docs/img/baiban.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

BIN
docs/img/cal.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 526 KiB

BIN
docs/img/doc.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

BIN
docs/img/excel.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

BIN
docs/img/file.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 456 KiB

BIN
docs/img/fileeditor.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

BIN
docs/img/gant.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

BIN
docs/img/home.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 513 KiB

BIN
docs/img/ie.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 543 KiB

BIN
docs/img/kanban.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

BIN
docs/img/localchat.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 426 KiB

BIN
docs/img/markdown.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 441 KiB

BIN
docs/img/mind.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

BIN
docs/img/pic.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

BIN
docs/img/ppt.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

BIN
docs/img/rili.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 600 KiB

BIN
docs/img/setting-store.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 KiB

4
frontend/src/components/desktop/Upgrade.vue

@ -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>

4
frontend/src/stores/upgrade.ts

@ -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,

18
godo/store/cmds.go

@ -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 {

47
godo/store/install.go

@ -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,21 +339,24 @@ 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 {
strValue := convertValueToString(value)
// 使用正则表达式替换Content中的占位符
storeInfo.Cmds[cmdGroupName][i].Content = pattern.ReplaceAllStringFunc(
cmd.Content,
func(match string) string {
// 检查占位符是否与Config中的键匹配
if strings.Trim(match, "{}") == key {
return strValue
}
return match
},
)
if cmd.Content != "" {
strValue := convertValueToString(value)
// 使用正则表达式替换Content中的占位符
storeInfo.Commands[cmdGroupName][i].Content = pattern.ReplaceAllStringFunc(
cmd.Content,
func(match string) string {
// 检查占位符是否与Config中的键匹配
if strings.Trim(match, "{}") == key {
return strValue
}
return match
},
)
}
}
}
}

3
godo/store/store.go

@ -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

24
godo/store/types.go

@ -21,13 +21,13 @@ type InstallInfo struct {
// StoreInfo 维护了应用程序商店的信息。
// 包含应用程序的名称、图标、配置信息等。
type StoreInfo struct {
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"` // 启动应用程序的信息。
Name string `json:"name"` // 应用程序商店的名称。
Icon string `json:"icon"` // 应用程序商店的图标路径。
Setting Setting `json:"setting"` // 应用程序商店的配置信息。
Config map[string]any `json:"config"` // 应用程序的配置信息映射。
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"` // 安装过程中需要执行的命令列表。
}

Loading…
Cancel
Save