init plugins

This commit is contained in:
godo 2024-07-25 08:20:02 +08:00
parent 3c1517f434
commit 86e49a26aa
63 changed files with 1457 additions and 434 deletions

View file

@ -3,12 +3,10 @@ package app
import ( import (
"context" "context"
"errors" "errors"
"godoos/cmd" cmd "godo/cmd"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"runtime" "runtime"
"strings"
wruntime "github.com/wailsapp/wails/v2/pkg/runtime" wruntime "github.com/wailsapp/wails/v2/pkg/runtime"
) )
@ -28,7 +26,7 @@ func NewApp() *App {
// so we can call the runtime methods // so we can call the runtime methods
func (a *App) Startup(ctx context.Context) { func (a *App) Startup(ctx context.Context) {
a.ctx = ctx a.ctx = ctx
go cmd.OsStart() cmd.OsStart()
} }
func (a *App) Shutdown(ctx context.Context) { func (a *App) Shutdown(ctx context.Context) {
a.ctx = ctx a.ctx = ctx
@ -44,35 +42,31 @@ func (a *App) OpenDirDialog() string {
return path return path
} }
func (a *App) GetAbsPath(path string) (string, error) {
var absPath string
var err error
if filepath.IsAbs(path) {
absPath = filepath.Clean(path)
} else {
absPath, err = filepath.Abs(filepath.Join(a.exDir, path))
if err != nil {
return "", err
}
}
absPath = strings.ReplaceAll(absPath, "/", string(os.PathSeparator))
//println("GetAbsPath:", absPath)
return absPath, nil
}
func (a *App) RestartApp() error { func (a *App) RestartApp() error {
if runtime.GOOS == "windows" { name, err := os.Executable()
name, err := os.Executable() if err != nil {
if err != nil { return err
return err }
}
exec.Command(name, os.Args[1:]...).Start() var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command(name, os.Args[1:]...)
case "darwin": // macOS
cmd = exec.Command("/usr/bin/open", name)
case "linux":
cmd = exec.Command(name, os.Args[1:]...)
// Optionally, you could use 'xdg-open' or 'gnome-open' etc.
// cmd = exec.Command("/usr/bin/gnome-open", name)
default:
return errors.New("unsupported OS")
}
if cmd != nil {
cmd.Start()
wruntime.Quit(a.ctx) wruntime.Quit(a.ctx)
return nil return nil
} }
return errors.New("unsupported OS")
}
func (a *App) GetPlatform() string { return errors.New("failed to restart application")
return runtime.GOOS
} }

View file

@ -1,132 +0,0 @@
package app
import (
"context"
"path/filepath"
"time"
"github.com/cavaliergopher/grab/v3"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
func (a *App) DownloadFile(path string, url string) error {
absPath, err := a.GetAbsPath(path)
if err != nil {
return err
}
_, err = grab.Get(absPath, url)
if err != nil {
return err
}
return nil
}
type DownloadStatus struct {
resp *grab.Response
cancel context.CancelFunc
Name string `json:"name"`
Path string `json:"path"`
Url string `json:"url"`
Transferred int64 `json:"transferred"`
Size int64 `json:"size"`
Speed float64 `json:"speed"`
Progress float64 `json:"progress"`
Downloading bool `json:"downloading"`
Done bool `json:"done"`
}
var downloadList []*DownloadStatus
func existsInDownloadList(path string, url string) bool {
for _, ds := range downloadList {
if ds.Path == path || ds.Url == url {
return true
}
}
return false
}
func (a *App) PauseDownload(url string) {
for _, ds := range downloadList {
if ds.Url == url {
if ds.cancel != nil {
ds.cancel()
}
ds.resp = nil
ds.Downloading = false
ds.Speed = 0
break
}
}
}
func (a *App) ContinueDownload(url string) {
for _, ds := range downloadList {
if ds.Url == url {
if !ds.Downloading && ds.resp == nil && !ds.Done {
ds.Downloading = true
req, err := grab.NewRequest(ds.Path, ds.Url)
if err != nil {
ds.Downloading = false
break
}
// if PauseDownload() is called before the request finished, ds.Downloading will be false
// if the user keeps clicking pause and resume, it may result in multiple requests being successfully downloaded at the same time
// so we have to create a context and cancel it when PauseDownload() is called
ctx, cancel := context.WithCancel(context.Background())
ds.cancel = cancel
req = req.WithContext(ctx)
resp := grab.DefaultClient.Do(req)
if resp != nil && resp.HTTPResponse != nil &&
resp.HTTPResponse.StatusCode >= 200 && resp.HTTPResponse.StatusCode < 300 {
ds.resp = resp
} else {
ds.Downloading = false
}
}
break
}
}
}
func (a *App) AddToDownloadList(path string, url string) {
absPath, err := a.GetAbsPath(path)
if err != nil {
return
}
if !existsInDownloadList(absPath, url) {
downloadList = append(downloadList, &DownloadStatus{
resp: nil,
Name: filepath.Base(path),
Path: absPath,
Url: url,
Downloading: false,
})
}
a.ContinueDownload(url)
}
func (a *App) DownloadLoop() {
ticker := time.NewTicker(500 * time.Millisecond)
go func() {
for {
<-ticker.C
for _, ds := range downloadList {
if ds.resp != nil {
ds.Transferred = ds.resp.BytesComplete()
ds.Size = ds.resp.Size()
ds.Speed = ds.resp.BytesPerSecond()
ds.Progress = 100 * ds.resp.Progress()
ds.Downloading = !ds.resp.IsComplete()
ds.Done = ds.resp.Progress() == 1
if !ds.Downloading {
ds.resp = nil
}
}
}
runtime.EventsEmit(a.ctx, "downloadList", downloadList)
}
}()
}

View file

@ -5,8 +5,6 @@ import (
"bytes" "bytes"
"io" "io"
"net/http" "net/http"
"os"
"os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
@ -21,6 +19,17 @@ type ProgressReader struct {
total int64 total int64
err error err error
} }
type DownloadStatus struct {
Name string `json:"name"`
Path string `json:"path"`
Url string `json:"url"`
Transferred int64 `json:"transferred"`
Size int64 `json:"size"`
Speed float64 `json:"speed"`
Progress float64 `json:"progress"`
Downloading bool `json:"downloading"`
Done bool `json:"done"`
}
func (pr *ProgressReader) Read(p []byte) (n int, err error) { func (pr *ProgressReader) Read(p []byte) (n int, err error) {
n, err = pr.reader.Read(p) n, err = pr.reader.Read(p)
@ -88,13 +97,6 @@ func (a *App) UpdateApp(url string) (broken bool, err error) {
return false, err return false, err
} }
// restart app // restart app
if runtime.GOOS == "windows" { a.RestartApp()
name, err := os.Executable()
if err != nil {
return false, err
}
exec.Command(name, os.Args[1:]...).Start()
wruntime.Quit(a.ctx)
}
return false, nil return false, nil
} }

View file

@ -7,9 +7,10 @@ export {}
/* prettier-ignore */ /* prettier-ignore */
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
AddApp: typeof import('./src/components/store/AddApp.vue')['default']
AppIcon: typeof import('./src/components/taskbar/AppIcon.vue')['default'] AppIcon: typeof import('./src/components/taskbar/AppIcon.vue')['default']
AppIconGroup: typeof import('./src/components/taskbar/AppIconGroup.vue')['default'] AppIconGroup: typeof import('./src/components/taskbar/AppIconGroup.vue')['default']
AppItem: typeof import('./src/components/builtin/AppItem.vue')['default'] AppItem: typeof import('./src/components/store/AppItem.vue')['default']
Battery: typeof import('./src/components/taskbar/Battery.vue')['default'] Battery: typeof import('./src/components/taskbar/Battery.vue')['default']
BatteryPop: typeof import('./src/components/taskbar/BatteryPop.vue')['default'] BatteryPop: typeof import('./src/components/taskbar/BatteryPop.vue')['default']
Browser: typeof import('./src/components/builtin/Browser.vue')['default'] Browser: typeof import('./src/components/builtin/Browser.vue')['default']
@ -106,7 +107,7 @@ declare module 'vue' {
StartMenu: typeof import('./src/components/taskbar/StartMenu.vue')['default'] StartMenu: typeof import('./src/components/taskbar/StartMenu.vue')['default']
StartOption: typeof import('./src/components/taskbar/StartOption.vue')['default'] StartOption: typeof import('./src/components/taskbar/StartOption.vue')['default']
StateIcon: typeof import('./src/components/taskbar/StateIcon.vue')['default'] StateIcon: typeof import('./src/components/taskbar/StateIcon.vue')['default']
Store: typeof import('./src/components/builtin/Store.vue')['default'] Store: typeof import('./src/components/store/Store.vue')['default']
Taskbar: typeof import('./src/components/taskbar/Taskbar.vue')['default'] Taskbar: typeof import('./src/components/taskbar/Taskbar.vue')['default']
UpPopover: typeof import('./src/components/computer/UpPopover.vue')['default'] UpPopover: typeof import('./src/components/computer/UpPopover.vue')['default']
UrlBrowser: typeof import('./src/components/builtin/UrlBrowser.vue')['default'] UrlBrowser: typeof import('./src/components/builtin/UrlBrowser.vue')['default']

View file

@ -1 +1 @@
7d93ddef64f0624cd1fa203b3d0350ee 0427c64c40c111c3bee3a5dd4c0d87ed

View file

@ -0,0 +1,59 @@
<script setup lang="ts">
import { ref } from "vue";
const formData = ref({
importType: 'remote'
})
const addType = [
{
'name' : '远程下载',
'type' : 'remote'
},
{
'name' : '本地导入',
'type' : 'local'
},
{
'name' : '开发模式',
'type' : 'dev'
}
]
</script>
<template>
<div class="setting">
<div class="setting-item">
<label>添加方式</label>
<el-select v-model="formData.importType" style="width: 180px;">
<el-option
v-for="item in addType"
:key="item.type"
:label="item.name"
:value="item.type"
/>
</el-select>
</div>
</div>
</template>
<style scoped>
.setting-item {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
margin-bottom: 20px;
gap: 20px;
}
.setting-item label {
display: block;
width: 100px;
flex-shrink: 0;
text-align: right;
}
.setting-item {
display: flex;
align-items: center;
}
</style>

View file

@ -1,30 +1,14 @@
<template> <template>
<div class="outer"> <div class="outer">
<div class="uper">
<div class="up-text">应用商店</div>
</div>
<div class="store-handle" v-dragable>
<div v-if="!closing" @click="closeWin" class="close-button">×</div>
</div>
<div class="main"> <div class="main">
<div class="left"> <div class="left">
<div class="left-icon"> <div class="left-icon" v-for="(item, key) in categoryList" @click="changeCate(key, item)">
<div class="icon-derc"></div> <div class="icon-derc" v-if="key == currentCateId"></div>
<svg <el-tooltip class="box-item" effect="dark" :content="t('store.' + item)" placement="right">
t="1694613650127" <el-icon size="22">
class="icon" <component :is="categoryIcon[key]" />
viewBox="0 0 1024 1024" </el-icon>
version="1.1" </el-tooltip>
xmlns="http://www.w3.org/2000/svg"
p-id="10617"
width="200"
height="200"
>
<path
d="M511.813 92.188L92.188 428.844v502.5h295.406V647.469h248.344v283.875h295.406v-502.5z"
p-id="10618"
></path>
</svg>
</div> </div>
</div> </div>
<div class="store"> <div class="store">
@ -32,9 +16,9 @@
<!-- <div class="left-bar"></div> --> <!-- <div class="left-bar"></div> -->
<div class="right-main"> <div class="right-main">
<div class="main-title"> <div class="main-title">
<!-- <span class="sub-title">热门应用 </span> --> <span class="sub-title">{{ currentTitle }} </span>
</div> </div>
<div class="swiper"> <!-- <div class="swiper">
<div class="swiper-txt">主页</div> <div class="swiper-txt">主页</div>
<div class="swiper-inner"> <div class="swiper-inner">
<div class="swiper-tab"> <div class="swiper-tab">
@ -46,20 +30,14 @@
<div class="swiper-tab"> <div class="swiper-tab">
<img src="/image/store/banner3.jpg" /> <img src="/image/store/banner3.jpg" />
</div> </div>
<div class="swiper-tab">
<img src="/image/store/banner2.jpg" />
</div>
</div> </div>
</div> </div> -->
<div class="main-app"> <div class="main-app">
<div v-for="item in storeList" class="store-item" :key="item.name"> <div v-for="item in storeList" v-if="currentTitle != t('store.add')" class="store-item" :key="item.name">
<AppItem <AppItem :item="item" :installed-list="installedList" :install="install" :uninstall="uninstall" />
:item="item" </div>
:installed-list="installedList" <AddApp v-else/>
:install="install"
:uninstall="uninstall"
/>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -77,23 +55,19 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { inject, onMounted, ref } from "vue"; import { inject, onMounted, ref } from "vue";
import { BrowserWindow, System, Dialog, vDragable } from "@/system"; import { System, Dialog } from "@/system";
import { t } from "@/i18n"; import { t } from "@/i18n";
import storeList from "@/assets/store.json"; //import storeList from "@/assets/store.json";
const browserWindow: BrowserWindow = inject("browserWindow")!;
import { getSystemKey, setSystemKey } from "@/system/config"; import { getSystemKey, setSystemKey } from "@/system/config";
const sys: any = inject<System>("system"); const sys: any = inject<System>("system");
console.log(storeList); const currentCateId = ref(0)
const currentTitle = ref(t("store.hots"))
const categoryList = ['hots', 'work', 'development', 'games', 'education', 'news', 'shopping', 'social', 'utilities', 'others', 'add']
const categoryIcon = ['HomeFilled', 'Odometer', 'Postcard', 'TrendCharts', 'School', 'HelpFilled', 'ShoppingCart', 'ChatLineRound', 'MessageBox', 'Ticket', 'CirclePlusFilled']
const isready = ref(false); const isready = ref(false);
const installed = getSystemKey("intstalledPlugins"); const installed = getSystemKey("intstalledPlugins");
const installedList: any = ref(installed); const installedList: any = ref(installed);
const closing = ref(false); const storeList: any = ref([])
function closeWin() {
closing.value = true;
setTimeout(() => {
browserWindow.close();
}, 200);
}
onMounted(() => { onMounted(() => {
setTimeout(() => { setTimeout(() => {
if (!isready.value) { if (!isready.value) {
@ -101,6 +75,10 @@ onMounted(() => {
} }
}, 1000); }, 1000);
}); });
function changeCate(index: number, item: string) {
currentCateId.value = index
currentTitle.value = t("store." + item)
}
function setCache() { function setCache() {
setSystemKey("intstalledPlugins", installedList.value); setSystemKey("intstalledPlugins", installedList.value);
//localStorage.setItem("godoOS_installedApp", JSON.stringify(installedList.value)) //localStorage.setItem("godoOS_installedApp", JSON.stringify(installedList.value))
@ -143,6 +121,7 @@ function uninstall(item: any) {
height: 16px; height: 16px;
background-color: #ffffff; background-color: #ffffff;
} }
/* /*
内阴影+圆角*/ 内阴影+圆角*/
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
@ -150,6 +129,7 @@ function uninstall(item: any) {
border-radius: 10px; border-radius: 10px;
background-color: #ffffff; background-color: #ffffff;
} }
/* /*
内阴影+圆角*/ 内阴影+圆角*/
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
@ -164,6 +144,7 @@ function uninstall(item: any) {
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
} }
.uper { .uper {
padding: 0px 20px; padding: 0px 20px;
font-size: 12px; font-size: 12px;
@ -173,6 +154,7 @@ function uninstall(item: any) {
color: rgb(134, 134, 134); color: rgb(134, 134, 134);
background-color: rgb(243, 243, 243); background-color: rgb(243, 243, 243);
} }
.store-handle { .store-handle {
width: 100%; width: 100%;
height: 40px; height: 40px;
@ -192,9 +174,10 @@ function uninstall(item: any) {
overflow: hidden; overflow: hidden;
background-color: rgb(243, 243, 243); background-color: rgb(243, 243, 243);
} }
.left { .left {
width: 60px; width: 60px;
height: 60px; height: auto;
flex-shrink: 0; flex-shrink: 0;
background-color: rgb(243, 243, 243); background-color: rgb(243, 243, 243);
display: flex; display: flex;
@ -202,6 +185,7 @@ function uninstall(item: any) {
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
} }
.left-icon { .left-icon {
width: 56px; width: 56px;
height: 50px; height: 50px;
@ -209,9 +193,14 @@ function uninstall(item: any) {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
background-color: white; background-color: white;
border-radius: 6px;
position: relative; position: relative;
cursor: pointer;
&:hover {
background-color: rgb(243, 243, 243);
}
} }
.icon-derc { .icon-derc {
height: 20px; height: 20px;
width: 4px; width: 4px;
@ -220,12 +209,14 @@ function uninstall(item: any) {
position: absolute; position: absolute;
left: 2px; left: 2px;
} }
.left-icon svg {
.left-icon .ep_icon {
width: 20px; width: 20px;
height: 20px; height: 20px;
color: #363533; color: #363533;
stroke: #363533; stroke: #363533;
} }
.store { .store {
/* width: 100%; */ /* width: 100%; */
flex: 1; flex: 1;
@ -242,10 +233,12 @@ function uninstall(item: any) {
align-items: flex-start; align-items: flex-start;
overflow: hidden; overflow: hidden;
} }
.left-bar { .left-bar {
width: 60px; width: 60px;
flex-shrink: 0; flex-shrink: 0;
} }
.right-main { .right-main {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -264,6 +257,7 @@ function uninstall(item: any) {
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
} }
.sub-title { .sub-title {
padding-top: 30px; padding-top: 30px;
font-size: 20px; font-size: 20px;
@ -273,12 +267,14 @@ function uninstall(item: any) {
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
} }
.sub-tip { .sub-tip {
font-size: 12px; font-size: 12px;
color: rgb(11, 31, 111); color: rgb(11, 31, 111);
margin-left: 10px; margin-left: 10px;
user-select: none; user-select: none;
} }
.swiper { .swiper {
width: max-content; width: max-content;
height: 300px; height: 300px;
@ -287,7 +283,9 @@ function uninstall(item: any) {
margin-top: 0px; margin-top: 0px;
position: relative; position: relative;
} }
@keyframes swiperAni { @keyframes swiperAni {
0%, 0%,
33% { 33% {
transform: translateX(0px); transform: translateX(0px);
@ -302,10 +300,12 @@ function uninstall(item: any) {
96% { 96% {
transform: translateX(-1200px); transform: translateX(-1200px);
} }
100% { 100% {
transform: translateX(0px); transform: translateX(0px);
} }
} }
.swiper-inner { .swiper-inner {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
@ -313,6 +313,7 @@ function uninstall(item: any) {
gap: 10px; gap: 10px;
animation: swiperAni 20s ease-in-out infinite; animation: swiperAni 20s ease-in-out infinite;
} }
.swiper-tab { .swiper-tab {
width: 600px; width: 600px;
height: 300px; height: 300px;
@ -320,12 +321,14 @@ function uninstall(item: any) {
border-radius: 20px; border-radius: 20px;
box-shadow: 0px 10px 20px 1px #2524241f; box-shadow: 0px 10px 20px 1px #2524241f;
} }
.swiper-tab img { .swiper-tab img {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 20px; border-radius: 20px;
object-fit: cover; object-fit: cover;
} }
.swiper-txt { .swiper-txt {
position: absolute; position: absolute;
top: 20px; top: 20px;
@ -335,6 +338,7 @@ function uninstall(item: any) {
z-index: 10; z-index: 10;
text-shadow: 0px 0px 5px #00000058; text-shadow: 0px 0px 5px #00000058;
} }
.main-app { .main-app {
width: 100%; width: 100%;
/* height: 100%; */ /* height: 100%; */
@ -347,6 +351,7 @@ function uninstall(item: any) {
overflow: auto; overflow: auto;
align-content: flex-start; align-content: flex-start;
} }
.store-noready { .store-noready {
width: 100%; width: 100%;
height: 100%; height: 100%;

View file

@ -130,5 +130,18 @@
"black": "Black", "black": "Black",
"cannot.create.shortcut": "Cannot Create Shortcut", "cannot.create.shortcut": "Cannot Create Shortcut",
"shortcut.has.been.created": "Shortcut Has Been Created", "shortcut.has.been.created": "Shortcut Has Been Created",
"system.message": "System Message" "system.message": "System Message",
"store" : {
"hots":"Popular",
"work": "Work",
"development": "Development",
"games": "Games",
"education": "Education",
"news": "News",
"shopping":"Shopping",
"social": "Social Networking",
"utilities": "Utilities",
"others": "Others",
"add":"Add"
}
} }

View file

@ -130,5 +130,18 @@
"black": "黑色", "black": "黑色",
"cannot.create.shortcut": "无法创建快捷方式", "cannot.create.shortcut": "无法创建快捷方式",
"shortcut.has.been.created": "快捷方式已创建", "shortcut.has.been.created": "快捷方式已创建",
"system.message": "系统消息" "system.message": "系统消息",
"store" : {
"hots":"热门",
"work": "办公",
"development": "开发工具",
"games": "游戏",
"education": "教育",
"news": "新闻",
"shopping":"购物",
"social": "社交",
"utilities": "实用工具",
"others": "其他",
"add":"添加应用"
}
} }

View file

@ -34,13 +34,13 @@ export const appList = [
multiple: false, multiple: false,
appIcon: "store", appIcon: "store",
content: "Store", content: "Store",
frame: false, frame: true,
width: 900, width: 900,
height: 630, height: 600,
center: true, center: true,
resizable: true, resizable: true,
backgroundColor: '#ffffff00', backgroundColor: '#ffffff00',
isDeskTop: false, isDeskTop: true,
isMagnet: true, isMagnet: true,
isMenuList: true, isMenuList: true,
}, },

View file

@ -1,22 +1,8 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export function AddToDownloadList(arg1:string,arg2:string):Promise<void>;
export function ContinueDownload(arg1:string):Promise<void>;
export function DownloadFile(arg1:string,arg2:string):Promise<void>;
export function DownloadLoop():Promise<void>;
export function GetAbsPath(arg1:string):Promise<string>;
export function GetPlatform():Promise<string>;
export function OpenDirDialog():Promise<string>; export function OpenDirDialog():Promise<string>;
export function PauseDownload(arg1:string):Promise<void>;
export function RestartApp():Promise<void>; export function RestartApp():Promise<void>;
export function UpdateApp(arg1:string):Promise<boolean>; export function UpdateApp(arg1:string):Promise<boolean>;

View file

@ -2,38 +2,10 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export function AddToDownloadList(arg1, arg2) {
return window['go']['app']['App']['AddToDownloadList'](arg1, arg2);
}
export function ContinueDownload(arg1) {
return window['go']['app']['App']['ContinueDownload'](arg1);
}
export function DownloadFile(arg1, arg2) {
return window['go']['app']['App']['DownloadFile'](arg1, arg2);
}
export function DownloadLoop() {
return window['go']['app']['App']['DownloadLoop']();
}
export function GetAbsPath(arg1) {
return window['go']['app']['App']['GetAbsPath'](arg1);
}
export function GetPlatform() {
return window['go']['app']['App']['GetPlatform']();
}
export function OpenDirDialog() { export function OpenDirDialog() {
return window['go']['app']['App']['OpenDirDialog'](); return window['go']['app']['App']['OpenDirDialog']();
} }
export function PauseDownload(arg1) {
return window['go']['app']['App']['PauseDownload'](arg1);
}
export function RestartApp() { export function RestartApp() {
return window['go']['app']['App']['RestartApp'](); return window['go']['app']['App']['RestartApp']();
} }

16
go.mod
View file

@ -1,19 +1,17 @@
module godoos module godoos
go 1.21 go 1.22.5
toolchain go1.22.5
require ( require (
github.com/cavaliergopher/grab/v3 v3.0.1
github.com/fsnotify/fsnotify v1.4.9
github.com/minio/selfupdate v0.6.0 github.com/minio/selfupdate v0.6.0
github.com/shirou/gopsutil v2.21.11+incompatible
github.com/wailsapp/wails/v2 v2.9.1 github.com/wailsapp/wails/v2 v2.9.1
) )
require ( require (
aead.dev/minisign v0.2.0 // indirect aead.dev/minisign v0.2.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect github.com/tklauser/numcpus v0.8.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
@ -24,7 +22,6 @@ require (
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.2.6 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/mux v1.8.1
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/labstack/echo/v4 v4.10.2 // indirect github.com/labstack/echo/v4 v4.10.2 // indirect
github.com/labstack/gommon v0.4.0 // indirect github.com/labstack/gommon v0.4.0 // indirect
@ -35,7 +32,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect github.com/rivo/uniseg v0.4.4 // indirect
github.com/samber/lo v1.38.1 // indirect github.com/samber/lo v1.38.1 // indirect
github.com/tkrajina/go-reflector v0.5.6 // indirect github.com/tkrajina/go-reflector v0.5.6 // indirect
@ -43,6 +40,7 @@ require (
github.com/valyala/fasttemplate v1.2.2 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wailsapp/go-webview2 v1.0.10 // indirect github.com/wailsapp/go-webview2 v1.0.10 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect
godo v0.0.1
golang.org/x/crypto v0.23.0 // indirect golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/net v0.25.0 // indirect golang.org/x/net v0.25.0 // indirect
@ -50,4 +48,6 @@ require (
golang.org/x/text v0.15.0 // indirect golang.org/x/text v0.15.0 // indirect
) )
replace godo v0.0.1 => ./godo
// replace github.com/wailsapp/wails/v2 v2.9.1 => /home/ruitao/go/pkg/mod // replace github.com/wailsapp/wails/v2 v2.9.1 => /home/ruitao/go/pkg/mod

15
go.sum
View file

@ -2,13 +2,11 @@ aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk=
aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ= aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
@ -56,12 +54,12 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/shirou/gopsutil v2.21.11+incompatible h1:lOGOyCG67a5dv2hq5Z1BLDUqqKp3HkbjPcz5j6XMS0U= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v2.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
@ -95,7 +93,6 @@ golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View file

@ -2,9 +2,10 @@ package cmd
import ( import (
"context" "context"
"godoos/libs" "godo/files"
"godoos/localchat" "godo/libs"
"godoos/progress" "godo/localchat"
"godo/progress"
"log" "log"
"net/http" "net/http"
"time" "time"
@ -22,6 +23,8 @@ func OsStart() {
router.Use(corsMiddleware()) router.Use(corsMiddleware())
// 使用带有日志装饰的处理器注册路由 // 使用带有日志装饰的处理器注册路由
router.Use(loggingMiddleware{}.Middleware) router.Use(loggingMiddleware{}.Middleware)
staticDir := libs.GetStaticDir()
router.PathPrefix("/static").Handler(http.StripPrefix("/static", http.FileServer(http.Dir(staticDir))))
progressRouter := router.PathPrefix("/progress").Subrouter() progressRouter := router.PathPrefix("/progress").Subrouter()
progressRouter.HandleFunc("/start/{name}", progress.StartProcess).Methods(http.MethodGet) progressRouter.HandleFunc("/start/{name}", progress.StartProcess).Methods(http.MethodGet)
progressRouter.HandleFunc("/stop/{name}", progress.StopProcess).Methods(http.MethodGet) progressRouter.HandleFunc("/stop/{name}", progress.StopProcess).Methods(http.MethodGet)
@ -35,22 +38,22 @@ func OsStart() {
progressRouter.HandleFunc("/app/{name}/{subpath:.*}", progress.ForwardRequest).Methods(http.MethodGet, http.MethodPost) progressRouter.HandleFunc("/app/{name}/{subpath:.*}", progress.ForwardRequest).Methods(http.MethodGet, http.MethodPost)
router.HandleFunc("/ping", progress.Ping).Methods(http.MethodGet) router.HandleFunc("/ping", progress.Ping).Methods(http.MethodGet)
router.HandleFunc("/", progress.Ping).Methods(http.MethodGet) router.HandleFunc("/", progress.Ping).Methods(http.MethodGet)
router.HandleFunc("/system/info", HandleSystemInfo).Methods(http.MethodGet) router.HandleFunc("/system/info", files.HandleSystemInfo).Methods(http.MethodGet)
router.HandleFunc("/system/setting", HandleSetConfig).Methods(http.MethodPost) router.HandleFunc("/system/setting", HandleSetConfig).Methods(http.MethodPost)
router.HandleFunc("/file/read", HandleReadDir).Methods(http.MethodGet) router.HandleFunc("/file/read", files.HandleReadDir).Methods(http.MethodGet)
router.HandleFunc("/file/stat", HandleStat).Methods(http.MethodGet) router.HandleFunc("/file/stat", files.HandleStat).Methods(http.MethodGet)
router.HandleFunc("/file/chmod", HandleChmod).Methods(http.MethodPost) router.HandleFunc("/file/chmod", files.HandleChmod).Methods(http.MethodPost)
router.HandleFunc("/file/exists", HandleExists).Methods(http.MethodGet) router.HandleFunc("/file/exists", files.HandleExists).Methods(http.MethodGet)
router.HandleFunc("/file/readfile", HandleReadFile).Methods(http.MethodGet) router.HandleFunc("/file/readfile", files.HandleReadFile).Methods(http.MethodGet)
router.HandleFunc("/file/unlink", HandleUnlink).Methods(http.MethodGet) router.HandleFunc("/file/unlink", files.HandleUnlink).Methods(http.MethodGet)
router.HandleFunc("/file/clear", HandleClear).Methods(http.MethodGet) router.HandleFunc("/file/clear", files.HandleClear).Methods(http.MethodGet)
router.HandleFunc("/file/rename", HandleRename).Methods(http.MethodGet) router.HandleFunc("/file/rename", files.HandleRename).Methods(http.MethodGet)
router.HandleFunc("/file/mkdir", HandleMkdir).Methods(http.MethodPost) router.HandleFunc("/file/mkdir", files.HandleMkdir).Methods(http.MethodPost)
router.HandleFunc("/file/rmdir", HandleRmdir).Methods(http.MethodGet) router.HandleFunc("/file/rmdir", files.HandleRmdir).Methods(http.MethodGet)
router.HandleFunc("/file/copyfile", HandleCopyFile).Methods(http.MethodGet) router.HandleFunc("/file/copyfile", files.HandleCopyFile).Methods(http.MethodGet)
router.HandleFunc("/file/writefile", HandleWriteFile).Methods(http.MethodPost) router.HandleFunc("/file/writefile", files.HandleWriteFile).Methods(http.MethodPost)
router.HandleFunc("/file/appendfile", HandleAppendFile).Methods(http.MethodPost) router.HandleFunc("/file/appendfile", files.HandleAppendFile).Methods(http.MethodPost)
router.HandleFunc("/file/watch", WatchHandler).Methods(http.MethodGet) router.HandleFunc("/file/watch", files.WatchHandler).Methods(http.MethodGet)
router.HandleFunc("/localchat/sse", localchat.SseHandler).Methods(http.MethodGet) router.HandleFunc("/localchat/sse", localchat.SseHandler).Methods(http.MethodGet)
router.HandleFunc("/localchat/message", localchat.HandleMessage).Methods(http.MethodPost) router.HandleFunc("/localchat/message", localchat.HandleMessage).Methods(http.MethodPost)
router.HandleFunc("/localchat/upload", localchat.MultiUploadHandler).Methods(http.MethodPost) router.HandleFunc("/localchat/upload", localchat.MultiUploadHandler).Methods(http.MethodPost)

View file

@ -2,35 +2,39 @@ package cmd
import ( import (
"encoding/json" "encoding/json"
"godoos/libs" "godo/libs"
"net/http" "net/http"
"os" "os"
) )
func GetOsPath() string {
osInfo, _ := libs.GetConfig("osInfo")
return osInfo.Value
}
func HandleSetConfig(w http.ResponseWriter, r *http.Request) { func HandleSetConfig(w http.ResponseWriter, r *http.Request) {
var req libs.ReqBody var req libs.ReqBody
err := json.NewDecoder(r.Body).Decode(&req) err := json.NewDecoder(r.Body).Decode(&req)
if err != nil { if err != nil {
ErrorMsg(w, "The params is error!") libs.ErrorMsg(w, "The params is error!")
return return
} }
if req.Name == "osInfo" { if req.Name == "osInfo" && req.Value != "" {
osInfo, _ := libs.GetConfig("osInfo") osInfo, _ := libs.GetConfig("osInfo")
if req.Value != "" { if osInfo.Value != req.Value {
if !libs.PathExists(req.Value) { if !libs.PathExists(req.Value) {
ErrorMsg(w, "The Path is not exists!") libs.ErrorMsg(w, "The Path is not exists!")
return return
} }
err = os.Chmod(req.Value, 0755) err = os.Chmod(req.Value, 0755)
if err != nil { if err != nil {
ErrorMsg(w, "The Path chmod is error!") libs.ErrorMsg(w, "The Path chmod is error!")
return return
} }
osInfo.Value = req.Value osInfo.Value = req.Value
osInfo.Type = req.Type
libs.SetConfig(osInfo)
} }
osInfo.Type = req.Type
libs.SetConfig(osInfo)
} }
if req.Name == "userInfo" || if req.Name == "userInfo" ||
req.Name == "dbInfo" { req.Name == "dbInfo" {
@ -38,8 +42,8 @@ func HandleSetConfig(w http.ResponseWriter, r *http.Request) {
} }
err = libs.LoadConfig() err = libs.LoadConfig()
if err != nil { if err != nil {
ErrorMsg(w, "The config load error!") libs.ErrorMsg(w, "The config load error!")
return return
} }
SuccessMsg(w, "success", "The config set success!") libs.SuccessMsg(w, "success", "The config set success!")
} }

View file

@ -1,10 +1,10 @@
package cmd package files
import ( import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"godoos/libs" "godo/libs"
"io" "io"
"log" "log"
"net/http" "net/http"
@ -18,12 +18,12 @@ import (
func HandleSystemInfo(w http.ResponseWriter, r *http.Request) { func HandleSystemInfo(w http.ResponseWriter, r *http.Request) {
info := libs.GetSystemInfo() info := libs.GetSystemInfo()
// res := APIResponse{ // res := libs.APIResponse{
// Message: "File information retrieved successfully.", // Message: "File information retrieved successfully.",
// Data: info, // Data: info,
// } // }
//json.NewEncoder(w).Encode(res) //json.NewEncoder(w).Encode(res)
SuccessMsg(w, info, "File information retrieved successfully.") libs.SuccessMsg(w, info, "File information retrieved successfully.")
} }
// HandleReadDir handles reading a directory // HandleReadDir handles reading a directory
@ -31,13 +31,13 @@ func HandleReadDir(w http.ResponseWriter, r *http.Request) {
path := r.URL.Query().Get("path") path := r.URL.Query().Get("path")
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
files, err := ReadDir(basePath, path) files, err := ReadDir(basePath, path)
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
@ -49,7 +49,7 @@ func HandleReadDir(w http.ResponseWriter, r *http.Request) {
} }
osFileInfo, err := GetFileInfo(entry, basePath, path) osFileInfo, err := GetFileInfo(entry, basePath, path)
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
@ -57,14 +57,14 @@ func HandleReadDir(w http.ResponseWriter, r *http.Request) {
if osFileInfo.IsFile { if osFileInfo.IsFile {
file, err := os.Open(filepath.Join(basePath, osFileInfo.Path)) file, err := os.Open(filepath.Join(basePath, osFileInfo.Path))
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to open file: %v", err)) libs.HTTPError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to open file: %v", err))
return return
} }
defer file.Close() defer file.Close()
content, err := io.ReadAll(file) content, err := io.ReadAll(file)
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to read file content: %v", err)) libs.HTTPError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to read file content: %v", err))
return return
} }
osFileInfo.Content = string(content) osFileInfo.Content = string(content)
@ -83,7 +83,7 @@ func HandleReadDir(w http.ResponseWriter, r *http.Request) {
sort.Slice(osFileInfos, func(i, j int) bool { sort.Slice(osFileInfos, func(i, j int) bool {
return osFileInfos[i].ModTime.Before(osFileInfos[j].ModTime) return osFileInfos[i].ModTime.Before(osFileInfos[j].ModTime)
}) })
res := APIResponse{ res := libs.APIResponse{
Message: "Directory read successfully.", Message: "Directory read successfully.",
Data: osFileInfos, Data: osFileInfos,
} }
@ -94,23 +94,23 @@ func HandleReadDir(w http.ResponseWriter, r *http.Request) {
func HandleStat(w http.ResponseWriter, r *http.Request) { func HandleStat(w http.ResponseWriter, r *http.Request) {
path := r.URL.Query().Get("path") path := r.URL.Query().Get("path")
if err := validateFilePath(path); err != nil { if err := validateFilePath(path); err != nil {
HTTPError(w, http.StatusBadRequest, err.Error()) libs.HTTPError(w, http.StatusBadRequest, err.Error())
return return
} }
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
// 调用GetFileInfo传入路径而不是fs.DirEntry // 调用GetFileInfo传入路径而不是fs.DirEntry
osFileInfo, err := GetFileInfo(path, basePath, "") osFileInfo, err := GetFileInfo(path, basePath, "")
if err != nil { if err != nil {
HTTPError(w, http.StatusNotFound, err.Error()) libs.HTTPError(w, http.StatusNotFound, err.Error())
return return
} }
res := APIResponse{ res := libs.APIResponse{
Message: "File information retrieved successfully.", Message: "File information retrieved successfully.",
Data: osFileInfo, Data: osFileInfo,
} }
@ -121,12 +121,12 @@ func HandleStat(w http.ResponseWriter, r *http.Request) {
func HandleExists(w http.ResponseWriter, r *http.Request) { func HandleExists(w http.ResponseWriter, r *http.Request) {
path := r.URL.Query().Get("path") path := r.URL.Query().Get("path")
if err := validateFilePath(path); err != nil { if err := validateFilePath(path); err != nil {
HTTPError(w, http.StatusBadRequest, err.Error()) libs.HTTPError(w, http.StatusBadRequest, err.Error())
return return
} }
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
exists := Exists(basePath, path) exists := Exists(basePath, path)
@ -134,7 +134,7 @@ func HandleExists(w http.ResponseWriter, r *http.Request) {
if exists { if exists {
message = "File exists." message = "File exists."
} }
res := APIResponse{ res := libs.APIResponse{
Message: message, Message: message,
Data: exists, Data: exists,
} }
@ -145,17 +145,17 @@ func HandleExists(w http.ResponseWriter, r *http.Request) {
func HandleReadFile(w http.ResponseWriter, r *http.Request) { func HandleReadFile(w http.ResponseWriter, r *http.Request) {
path := r.URL.Query().Get("path") path := r.URL.Query().Get("path")
if err := validateFilePath(path); err != nil { if err := validateFilePath(path); err != nil {
HTTPError(w, http.StatusBadRequest, err.Error()) libs.HTTPError(w, http.StatusBadRequest, err.Error())
return return
} }
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
fileContent, err := ReadFile(basePath, path) fileContent, err := ReadFile(basePath, path)
if err != nil { if err != nil {
HTTPError(w, http.StatusNotFound, err.Error()) libs.HTTPError(w, http.StatusNotFound, err.Error())
return return
} }
content := string(fileContent) content := string(fileContent)
@ -163,7 +163,7 @@ func HandleReadFile(w http.ResponseWriter, r *http.Request) {
if !strings.HasPrefix(content, "link::") { if !strings.HasPrefix(content, "link::") {
content = base64.StdEncoding.EncodeToString(fileContent) content = base64.StdEncoding.EncodeToString(fileContent)
} }
res := APIResponse{ res := libs.APIResponse{
Message: fmt.Sprintf("File '%s' read successfully.", path), Message: fmt.Sprintf("File '%s' read successfully.", path),
Data: content, // Optionally, encode content as base64 for transmission Data: content, // Optionally, encode content as base64 for transmission
//Data: string(fileContent), //Data: string(fileContent),
@ -175,20 +175,20 @@ func HandleReadFile(w http.ResponseWriter, r *http.Request) {
func HandleUnlink(w http.ResponseWriter, r *http.Request) { func HandleUnlink(w http.ResponseWriter, r *http.Request) {
path := r.URL.Query().Get("path") path := r.URL.Query().Get("path")
if err := validateFilePath(path); err != nil { if err := validateFilePath(path); err != nil {
HTTPError(w, http.StatusBadRequest, err.Error()) libs.HTTPError(w, http.StatusBadRequest, err.Error())
return return
} }
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
err = Unlink(basePath, path) err = Unlink(basePath, path)
if err != nil { if err != nil {
HTTPError(w, http.StatusConflict, err.Error()) libs.HTTPError(w, http.StatusConflict, err.Error())
return return
} }
res := APIResponse{Message: fmt.Sprintf("File '%s' successfully removed.", path)} res := libs.APIResponse{Message: fmt.Sprintf("File '%s' successfully removed.", path)}
json.NewEncoder(w).Encode(res) json.NewEncoder(w).Encode(res)
} }
@ -196,15 +196,15 @@ func HandleUnlink(w http.ResponseWriter, r *http.Request) {
func HandleClear(w http.ResponseWriter, r *http.Request) { func HandleClear(w http.ResponseWriter, r *http.Request) {
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
err = Clear(basePath) err = Clear(basePath)
if err != nil { if err != nil {
HTTPError(w, http.StatusConflict, err.Error()) libs.HTTPError(w, http.StatusConflict, err.Error())
return return
} }
res := APIResponse{Message: "FileSystem successfully cleared."} res := libs.APIResponse{Message: "FileSystem successfully cleared."}
json.NewEncoder(w).Encode(res) json.NewEncoder(w).Encode(res)
} }
@ -213,21 +213,21 @@ func HandleRename(w http.ResponseWriter, r *http.Request) {
oldPath := r.URL.Query().Get("oldPath") oldPath := r.URL.Query().Get("oldPath")
newPath := r.URL.Query().Get("newPath") newPath := r.URL.Query().Get("newPath")
if err := validateFilePathPair(oldPath, newPath); err != nil { if err := validateFilePathPair(oldPath, newPath); err != nil {
HTTPError(w, http.StatusBadRequest, err.Error()) libs.HTTPError(w, http.StatusBadRequest, err.Error())
return return
} }
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
err = Rename(basePath, oldPath, newPath) err = Rename(basePath, oldPath, newPath)
if err != nil { if err != nil {
log.Printf("Error renaming file: %s", err.Error()) log.Printf("Error renaming file: %s", err.Error())
HTTPError(w, http.StatusConflict, err.Error()) libs.HTTPError(w, http.StatusConflict, err.Error())
return return
} }
res := APIResponse{Message: fmt.Sprintf("File '%s' successfully renamed to '%s'.", oldPath, newPath)} res := libs.APIResponse{Message: fmt.Sprintf("File '%s' successfully renamed to '%s'.", oldPath, newPath)}
json.NewEncoder(w).Encode(res) json.NewEncoder(w).Encode(res)
} }
@ -235,20 +235,20 @@ func HandleRename(w http.ResponseWriter, r *http.Request) {
func HandleMkdir(w http.ResponseWriter, r *http.Request) { func HandleMkdir(w http.ResponseWriter, r *http.Request) {
dirPath := r.URL.Query().Get("dirPath") dirPath := r.URL.Query().Get("dirPath")
if err := validateFilePath(dirPath); err != nil { if err := validateFilePath(dirPath); err != nil {
HTTPError(w, http.StatusBadRequest, err.Error()) libs.HTTPError(w, http.StatusBadRequest, err.Error())
return return
} }
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
err = Mkdir(basePath, dirPath) err = Mkdir(basePath, dirPath)
if err != nil { if err != nil {
HTTPError(w, http.StatusConflict, err.Error()) libs.HTTPError(w, http.StatusConflict, err.Error())
return return
} }
res := APIResponse{Message: fmt.Sprintf("Directory '%s' created successfully.", dirPath)} res := libs.APIResponse{Message: fmt.Sprintf("Directory '%s' created successfully.", dirPath)}
json.NewEncoder(w).Encode(res) json.NewEncoder(w).Encode(res)
} }
@ -256,20 +256,20 @@ func HandleMkdir(w http.ResponseWriter, r *http.Request) {
func HandleRmdir(w http.ResponseWriter, r *http.Request) { func HandleRmdir(w http.ResponseWriter, r *http.Request) {
dirPath := r.URL.Query().Get("dirPath") dirPath := r.URL.Query().Get("dirPath")
if err := validateFilePath(dirPath); err != nil { if err := validateFilePath(dirPath); err != nil {
HTTPError(w, http.StatusBadRequest, err.Error()) libs.HTTPError(w, http.StatusBadRequest, err.Error())
return return
} }
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
err = Rmdir(basePath, dirPath) err = Rmdir(basePath, dirPath)
if err != nil { if err != nil {
HTTPError(w, http.StatusConflict, err.Error()) libs.HTTPError(w, http.StatusConflict, err.Error())
return return
} }
res := APIResponse{Message: fmt.Sprintf("Directory '%s' successfully removed.", dirPath)} res := libs.APIResponse{Message: fmt.Sprintf("Directory '%s' successfully removed.", dirPath)}
json.NewEncoder(w).Encode(res) json.NewEncoder(w).Encode(res)
} }
@ -278,20 +278,20 @@ func HandleCopyFile(w http.ResponseWriter, r *http.Request) {
srcPath := r.URL.Query().Get("srcPath") srcPath := r.URL.Query().Get("srcPath")
dstPath := r.URL.Query().Get("dstPath") dstPath := r.URL.Query().Get("dstPath")
if err := validateFilePathPair(srcPath, dstPath); err != nil { if err := validateFilePathPair(srcPath, dstPath); err != nil {
HTTPError(w, http.StatusBadRequest, err.Error()) libs.HTTPError(w, http.StatusBadRequest, err.Error())
return return
} }
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
err = CopyFile(filepath.Join(basePath, srcPath), filepath.Join(basePath, dstPath)) err = CopyFile(filepath.Join(basePath, srcPath), filepath.Join(basePath, dstPath))
if err != nil { if err != nil {
HTTPError(w, http.StatusConflict, err.Error()) libs.HTTPError(w, http.StatusConflict, err.Error())
return return
} }
res := APIResponse{Message: fmt.Sprintf("File '%s' successfully copied to '%s'.", srcPath, dstPath)} res := libs.APIResponse{Message: fmt.Sprintf("File '%s' successfully copied to '%s'.", srcPath, dstPath)}
json.NewEncoder(w).Encode(res) json.NewEncoder(w).Encode(res)
} }
@ -300,7 +300,7 @@ func HandleWriteFile(w http.ResponseWriter, r *http.Request) {
filePath := r.URL.Query().Get("filePath") filePath := r.URL.Query().Get("filePath")
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
// 获取文件内容 // 获取文件内容
@ -324,7 +324,7 @@ func HandleWriteFile(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusConflict) http.Error(w, err.Error(), http.StatusConflict)
return return
} }
res := APIResponse{Message: fmt.Sprintf("File '%s' successfully written.", filePath)} res := libs.APIResponse{Message: fmt.Sprintf("File '%s' successfully written.", filePath)}
json.NewEncoder(w).Encode(res) json.NewEncoder(w).Encode(res)
} }
@ -332,25 +332,25 @@ func HandleWriteFile(w http.ResponseWriter, r *http.Request) {
func HandleAppendFile(w http.ResponseWriter, r *http.Request) { func HandleAppendFile(w http.ResponseWriter, r *http.Request) {
filePath := r.URL.Query().Get("filePath") filePath := r.URL.Query().Get("filePath")
if err := validateFilePath(filePath); err != nil { if err := validateFilePath(filePath); err != nil {
HTTPError(w, http.StatusBadRequest, err.Error()) libs.HTTPError(w, http.StatusBadRequest, err.Error())
return return
} }
content, _, err := r.FormFile("content") content, _, err := r.FormFile("content")
if err != nil { if err != nil {
HTTPError(w, http.StatusBadRequest, err.Error()) libs.HTTPError(w, http.StatusBadRequest, err.Error())
return return
} }
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
err = AppendToFile(filepath.Join(basePath, filePath), content) err = AppendToFile(filepath.Join(basePath, filePath), content)
if err != nil { if err != nil {
HTTPError(w, http.StatusConflict, err.Error()) libs.HTTPError(w, http.StatusConflict, err.Error())
return return
} }
res := APIResponse{Message: fmt.Sprintf("Content appended to file '%s'.", filePath)} res := libs.APIResponse{Message: fmt.Sprintf("Content appended to file '%s'.", filePath)}
json.NewEncoder(w).Encode(res) json.NewEncoder(w).Encode(res)
} }
@ -364,7 +364,7 @@ func HandleChmod(w http.ResponseWriter, r *http.Request) {
// 解析请求体 // 解析请求体
err := json.NewDecoder(r.Body).Decode(&reqData) err := json.NewDecoder(r.Body).Decode(&reqData)
if err != nil { if err != nil {
HTTPError(w, http.StatusBadRequest, "Failed to parse request body") libs.HTTPError(w, http.StatusBadRequest, "Failed to parse request body")
return return
} }
@ -373,29 +373,29 @@ func HandleChmod(w http.ResponseWriter, r *http.Request) {
modeStr := reqData.Mode modeStr := reqData.Mode
if err := validateFilePath(path); err != nil { if err := validateFilePath(path); err != nil {
HTTPError(w, http.StatusBadRequest, err.Error()) libs.HTTPError(w, http.StatusBadRequest, err.Error())
return return
} }
mode, err := parseMode(modeStr) mode, err := parseMode(modeStr)
if err != nil { if err != nil {
HTTPError(w, http.StatusBadRequest, fmt.Sprintf("Invalid mode: %s", err.Error())) libs.HTTPError(w, http.StatusBadRequest, fmt.Sprintf("Invalid mode: %s", err.Error()))
return return
} }
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
err = Chmod(basePath, path, mode) err = Chmod(basePath, path, mode)
if err != nil { if err != nil {
HTTPError(w, http.StatusConflict, err.Error()) libs.HTTPError(w, http.StatusConflict, err.Error())
return return
} }
res := APIResponse{Message: fmt.Sprintf("Permissions of file '%s' successfully changed to %o.", path, mode)} res := libs.APIResponse{Message: fmt.Sprintf("Permissions of file '%s' successfully changed to %o.", path, mode)}
json.NewEncoder(w).Encode(res) json.NewEncoder(w).Encode(res)
} }

View file

@ -1,12 +1,10 @@
package cmd package files
import ( import (
"encoding/json"
"fmt" "fmt"
"godoos/libs" "godo/libs"
"io" "io"
"io/fs" "io/fs"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -14,12 +12,6 @@ import (
) )
// Common response structure // Common response structure
type APIResponse struct {
Message string `json:"message"`
Code int `json:"code"`
Data any `json:"data,omitempty"`
Error string `json:"error,omitempty"`
}
type OsFileInfo struct { type OsFileInfo struct {
IsFile bool `json:"isFile"` IsFile bool `json:"isFile"`
@ -40,23 +32,6 @@ type OsFileInfo struct {
ID int `json:"id,omitempty"` // 文件ID可选 ID int `json:"id,omitempty"` // 文件ID可选
} }
func writeJSONResponse(w http.ResponseWriter, res APIResponse, status int) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(res)
}
// HTTPError 返回带有JSON错误消息的HTTP错误
func HTTPError(w http.ResponseWriter, status int, message string) {
writeJSONResponse(w, APIResponse{Message: message, Code: -1}, status)
}
func ErrorMsg(w http.ResponseWriter, message string) {
writeJSONResponse(w, APIResponse{Message: message, Code: -1}, 200)
}
func SuccessMsg(w http.ResponseWriter, data any, message string) {
writeJSONResponse(w, APIResponse{Message: message, Data: data, Code: 0}, 200)
}
// validateFilePath 验证路径不为空 // validateFilePath 验证路径不为空
func validateFilePath(path string) error { func validateFilePath(path string) error {
if path == "" { if path == "" {

View file

@ -1,10 +1,10 @@
package cmd package files
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"godoos/libs" "godo/libs"
"log" "log"
"net/http" "net/http"
"path/filepath" "path/filepath"
@ -117,12 +117,12 @@ func WatchHandler(w http.ResponseWriter, r *http.Request) {
dirToWatch := r.URL.Query().Get("filePath") dirToWatch := r.URL.Query().Get("filePath")
basePath, err := libs.GetOsDir() basePath, err := libs.GetOsDir()
if err != nil { if err != nil {
HTTPError(w, http.StatusInternalServerError, err.Error()) libs.HTTPError(w, http.StatusInternalServerError, err.Error())
return return
} }
filePath := filepath.Join(basePath, dirToWatch) filePath := filepath.Join(basePath, dirToWatch)
if !libs.PathExists(filePath) { if !libs.PathExists(filePath) {
HTTPError(w, http.StatusNotFound, "filepath is not exist!") libs.HTTPError(w, http.StatusNotFound, "filepath is not exist!")
return return
} }
StartSSEStream(w, r, filePath) StartSSEStream(w, r, filePath)

20
godo/go.mod Normal file
View file

@ -0,0 +1,20 @@
module godo
go 1.22.5
require (
github.com/fsnotify/fsnotify v1.7.0
github.com/gorilla/mux v1.8.1
github.com/pkg/errors v0.9.1
github.com/shirou/gopsutil v3.21.11+incompatible
)
require (
github.com/cavaliergopher/grab/v3 v3.0.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/sys v0.19.0 // indirect
)

29
godo/go.sum Normal file
View file

@ -0,0 +1,29 @@
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -11,11 +11,10 @@ import (
var reqBodyMap = sync.Map{} var reqBodyMap = sync.Map{}
type ReqBody struct { type ReqBody struct {
Name string `json:"name"` Name string `json:"name"`
Value string `json:"value"` Value string `json:"value"`
Type string `json:"type"` Type string `json:"type"`
Info map[string]string `json:"info"` Info map[string]string `json:"info"`
Plugins map[string]interface{} `json:"plugins"`
} }
func GetConfigFile() (string, error) { func GetConfigFile() (string, error) {

View file

@ -56,14 +56,7 @@ func GetAppDir() (string, error) {
return filepath.Join(homeDir, ".godoos"), nil return filepath.Join(homeDir, ".godoos"), nil
} }
// func OllamaModelsDir() string { func GetRunDir() string {
// homeDir, err := os.UserHomeDir()
// if err != nil {
// return ".godoos"
// }
// return filepath.Join(homeDir, ".godoos", "models")
// }
func GetExeDir() string {
// 获取当前用户主目录 // 获取当前用户主目录
homeDir, err := GetAppDir() homeDir, err := GetAppDir()
if err != nil { if err != nil {
@ -80,6 +73,28 @@ func GetExeDir() string {
} }
return filepath.Join(homeDir, "run", osType) return filepath.Join(homeDir, "run", osType)
} }
func GetStaticDir() string {
homeDir, err := GetAppDir()
if err != nil {
return "static"
}
staticPath := filepath.Join(homeDir, "static")
if !PathExists(staticPath) {
os.MkdirAll(staticPath, 0755)
}
return staticPath
}
func GetCacheDir() string {
homeDir, err := GetAppDir()
if err != nil {
return "cache"
}
cachePath := filepath.Join(homeDir, "cache")
if !PathExists(cachePath) {
os.MkdirAll(cachePath, 0755)
}
return cachePath
}
func PathExists(dir string) bool { func PathExists(dir string) bool {
_, err := os.Stat(dir) _, err := os.Stat(dir)
if err == nil { if err == nil {

30
godo/libs/msg.go Normal file
View file

@ -0,0 +1,30 @@
package libs
import (
"encoding/json"
"net/http"
)
type APIResponse struct {
Message string `json:"message"`
Code int `json:"code"`
Data any `json:"data,omitempty"`
Error string `json:"error,omitempty"`
}
func writeJSONResponse(w http.ResponseWriter, res APIResponse, status int) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(res)
}
// HTTPError 返回带有JSON错误消息的HTTP错误
func HTTPError(w http.ResponseWriter, status int, message string) {
writeJSONResponse(w, APIResponse{Message: message, Code: -1}, status)
}
func ErrorMsg(w http.ResponseWriter, message string) {
writeJSONResponse(w, APIResponse{Message: message, Code: -1}, 200)
}
func SuccessMsg(w http.ResponseWriter, data any, message string) {
writeJSONResponse(w, APIResponse{Message: message, Data: data, Code: 0}, 200)
}

View file

@ -1,7 +1,7 @@
package localchat package localchat
import ( import (
"godoos/libs" "godo/libs"
"os" "os"
"path/filepath" "path/filepath"
"time" "time"

View file

@ -71,7 +71,7 @@ func SseHandler(w http.ResponseWriter, r *http.Request) {
Message: time.Now().Format("2006-01-02 15:04:05"), Message: time.Now().Format("2006-01-02 15:04:05"),
} }
userList = append(userList, myMsg) userList = append(userList, myMsg)
log.Printf("Online users: %v", OnlineUsers) //log.Printf("Online users: %v", OnlineUsers)
for ip, info := range OnlineUsers { for ip, info := range OnlineUsers {
if ip != myIP { // 确保不重复添加自己 if ip != myIP { // 确保不重复添加自己
userList = append(userList, info) userList = append(userList, info)
@ -105,7 +105,7 @@ func HandleMessage(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close() defer r.Body.Close()
// 将消息放入messageChan // 将消息放入messageChan
messageChan <- msg messageChan <- msg
log.Printf("Received text message from %s: %s", msg.SenderInfo.IP, msg.Content) //log.Printf("Received text message from %s: %s", msg.SenderInfo.IP, msg.Content)
// 这里可以添加存储文本消息到数据库或其他处理逻辑 // 这里可以添加存储文本消息到数据库或其他处理逻辑
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Text message send successfully") fmt.Fprintln(w, "Text message send successfully")

View file

@ -56,6 +56,8 @@ var (
var uploadStatus = UploadStatus{Status: make(map[string]int)} var uploadStatus = UploadStatus{Status: make(map[string]int)}
var broadcartTime = 3 * time.Second var broadcartTime = 3 * time.Second
var broadcastAddr = "224.0.0.1:1679" // 多播地址 // var broadcastAddr = "224.0.0.1:1679" // 多播地址
var broadcastAddr = "224.0.0.251:1234"
// var broadcastAddr = "255.255.255.255:1769" // 广播地址 // var broadcastAddr = "255.255.255.255:1769" // 广播地址
var OnlineUsers = make(map[string]UdpMessage) // 全局mapkey为IPvalue为主机名 var OnlineUsers = make(map[string]UdpMessage) // 全局mapkey为IPvalue为主机名

View file

@ -3,7 +3,7 @@ package localchat
import ( import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"godoos/libs" "godo/libs"
"io" "io"
"log" "log"
"net/http" "net/http"

View file

@ -25,13 +25,6 @@ func StartServiceDiscovery() {
return return
} }
defer conn.Close() defer conn.Close()
// 设置组播通信的网路接口
// intf,err := net.InterfaceByName("eth0")
// if err != nil {
// fmt.Println("Error getting network interface:", err)
// return
// }
// conn.setMulticastInterface(intf)
buffer := make([]byte, 1024) buffer := make([]byte, 1024)
for { for {

7
godo/main.go Normal file
View file

@ -0,0 +1,7 @@
package main
import "godo/cmd"
func main() {
cmd.OsStart()
}

View file

@ -2,7 +2,7 @@ package progress
import ( import (
"fmt" "fmt"
"godoos/libs" "godo/libs"
"log" "log"
"net/http" "net/http"
"os" "os"
@ -44,7 +44,7 @@ func StartProcess(w http.ResponseWriter, r *http.Request) {
func ExecuteStartAll() error { func ExecuteStartAll() error {
log.Println("Starting all processes...") log.Println("Starting all processes...")
userExeDir := libs.GetExeDir() userExeDir := libs.GetRunDir()
userFis, err := os.ReadDir(userExeDir) userFis, err := os.ReadDir(userExeDir)
if err != nil { if err != nil {
@ -105,7 +105,7 @@ func ExecuteScript(name string) error {
binaryExt = ".exe" binaryExt = ".exe"
} }
scriptName := name + binaryExt scriptName := name + binaryExt
exeDir := libs.GetExeDir() // 获取执行程序的目录 exeDir := libs.GetRunDir() // 获取执行程序的目录
scriptPath := filepath.Join(exeDir, name, scriptName) // 拼接脚本的完整路径 scriptPath := filepath.Join(exeDir, name, scriptName) // 拼接脚本的完整路径
// 检查脚本文件的存在性 // 检查脚本文件的存在性
if !PathExists(scriptPath) { if !PathExists(scriptPath) {

159
godo/store/download.go Normal file
View file

@ -0,0 +1,159 @@
package store
import (
"context"
"encoding/json"
"godo/libs"
"io"
"log"
"net/http"
"path/filepath"
"sync"
"time"
"github.com/cavaliergopher/grab/v3"
)
type DownloadStatus struct {
resp *grab.Response
cancel context.CancelFunc
Name string `json:"name"`
Path string `json:"path"`
Url string `json:"url"`
Current int64 `json:"current"`
Size int64 `json:"size"`
Speed float64 `json:"speed"`
Progress float64 `json:"progress"`
Downloading bool `json:"downloading"`
Done bool `json:"done"`
}
const (
concurrency = 6 // 并发下载数
)
// var downloads = make(map[string]*grab.Response)
var downloadsMutex sync.Mutex
var downloadList map[string]*DownloadStatus
func existsInDownloadList(url string) bool {
_, ok := downloadList[url]
return ok
}
func PauseDownload(url string) {
downloadsMutex.Lock()
defer downloadsMutex.Unlock()
ds, ok := downloadList[url]
if ds.Url == url && ok {
if ds.cancel != nil {
ds.cancel()
}
ds.resp = nil
ds.Downloading = false
ds.Speed = 0
}
}
func ContinueDownload(url string) {
ds, ok := downloadList[url]
if ds.Url == url && ok {
if !ds.Downloading && ds.resp == nil && !ds.Done {
ds.Downloading = true
req, err := grab.NewRequest(ds.Path, ds.Url)
if err != nil {
ds.Downloading = false
return
}
ctx, cancel := context.WithCancel(context.Background())
ds.cancel = cancel
req = req.WithContext(ctx)
client := grab.NewClient()
client.HTTPClient = &http.Client{
Transport: &http.Transport{
MaxIdleConnsPerHost: concurrency, // 设置并发连接数
},
}
//resp := grab.DefaultClient.Do(req)
resp := client.Do(req)
if resp != nil && resp.HTTPResponse != nil &&
resp.HTTPResponse.StatusCode >= 200 && resp.HTTPResponse.StatusCode < 300 {
ds.resp = resp
} else {
ds.Downloading = false
}
}
}
}
func Download(url string) {
chacheDir := libs.GetCacheDir()
absPath := filepath.Join(chacheDir, filepath.Base(url))
if !existsInDownloadList(url) {
downloadList[url] = &DownloadStatus{
resp: nil,
Name: filepath.Base(url),
Path: absPath,
Url: url,
Downloading: false,
}
}
ContinueDownload(url)
}
func GetDownload(url string) *DownloadStatus {
downloadsMutex.Lock()
defer downloadsMutex.Unlock()
ds, ok := downloadList[url]
if ds.resp != nil && ok {
ds.Current = ds.resp.BytesComplete()
ds.Size = ds.resp.Size()
ds.Speed = ds.resp.BytesPerSecond()
ds.Progress = 100 * ds.resp.Progress()
ds.Downloading = !ds.resp.IsComplete()
ds.Done = ds.resp.Progress() == 1
if !ds.Downloading {
ds.resp = nil
}
}
if ds.Done {
delete(downloadList, url)
}
return ds
}
func DownloadHandler(w http.ResponseWriter, r *http.Request) {
url := r.URL.Query().Get("url")
Download(url)
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
flusher, ok := w.(http.Flusher)
if !ok {
log.Printf("Streaming unsupported")
http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
return
}
go func() {
for {
<-ticker.C
ds := GetDownload(url)
jsonBytes, err := json.Marshal(ds)
if err != nil {
log.Printf("Error marshaling FileProgress to JSON: %v", err)
continue
}
if w != nil {
io.WriteString(w, string(jsonBytes))
w.Write([]byte("\n"))
flusher.Flush()
} else {
log.Println("ResponseWriter is nil, cannot send progress")
}
if ds.Done {
return
}
}
}()
}

3
plugins/clock/README.md Normal file
View file

@ -0,0 +1,3 @@
# 番茄钟
- godoos应用商店Demo

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,206 @@
/* 设置字体 */
@font-face {
font-family: 'lcd';
src: url('../font/lcd.ttf') format('truetype');
}
@font-face {
font-family: 'icon';
src: url('../font/iconfont.ttf') format('truetype');
}
/*css 初始化 */
html, body, ul, li, ol, dl, dd, dt, p, h1, h2, h3, h4, h5, h6, form, fieldset, legend, img { margin:0; padding:0; }
fieldset, img,input,button { font-family:"Times New Roman","Microsoft YaHei","SimSun"; ;border:none; padding:0;margin:0;outline-style:none; } /*去掉边框、去掉轮廓(比如输入框外面的蓝边框)*/
/*去掉列表前面的圆点*/
ul, ol {
list-style: none;
}
/* 设置隐藏 */
.none {
display: none;
}
.flex {
display: flex;
}
/* 设置版心并居中 */
.w {
position: absolute;
top:50%;
left:50%;
transform: translate(-50%,-50%);
width: 95vw;
}
/* 设置字体 */
.iconfont {
font-family:"icon" !important;
font-style:normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.2px;
-moz-osx-font-smoothing: grayscale;
}
#n1,#n2,#n4,#n5{
width: 23%;
}
#n3 {
width: 8%;
transform: translate(20%,-25%);
}
.lcd {
font-family:"lcd" !important;
font-size:45vw;font-style:normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.2px;
-moz-osx-font-smoothing: grayscale;
}
/* 帮助界面 */
.p0 {
margin: 0 auto;
padding: 0 14vw;
position: relative;
}
h3 {
position: absolute;
left: 50%;
top: -7vw;
transform: translateX(-50%);
font-size: 4vw;
}
.tip {
border: 1px solid #999;
}
.p0 p {
margin: 2vw 0 ;
text-align: center;
font-size: 3vw;
}
/* 开始第一页 */
.top {
position: relative;
z-index: 999;
}
.top div{
width: 33.3%;
font-size: 3vw;
}
.top-left {
text-align: left;
}
.top-center {
text-align: center;
}
.top-right {
text-align: right;
white-space: nowrap;
}
.footer div{
width: 33.3%;
text-align: center;
font-size: 5vw;
}
.main {
margin: 1.5vw 0 2.5vw;
padding: 1vw 3vw;
border-top: 1px solid black;
border-bottom: 1px solid black;
}
/* 开始第二页 */
ol {
margin: 0 auto;
width: 50vw;
}
ol li {
text-align: center;
line-height: 7.4vw;
font-size: 4vw;
border-bottom: 1px solid #e1e1e1;
}
ol li:nth-last-child(1){
border-bottom: none;
}
/* 开始第三页 */
.p3 {
font-size: 4vw;
padding: 0 20%;
overflow: hidden;
}
button{
background-color: #fff;
border: 1px solid black;
font-size: 4vw;
padding: 0.2vw 0.7vw;
}
.line1,.line2 {
margin: 5vw auto;
display: flex;
justify-content: space-between;
}
.line1 {
margin-top: 0;
}
.p3 input{
width: 100%;
text-align: center;
font-size: 4vw;
border: 1px solid black;
}
.p3-main span{
width: 50%;
}
.p3-bottom {
padding: 0 2vw;
display: flex;
justify-content: space-between;
}
.p3-bottom button{
width: 40%;
}
/* 第四页 */
.p4 {
font-size: 4vw;
padding: 0 20%;
overflow: hidden;
}
.p4 input{
width: 100%;
text-align: center;
font-size: 4vw;
border: 1px solid black;
}
.p4-main span{
width: 50%;
}
.p4-bottom {
padding: 0 2vw;
display: flex;
justify-content: space-between;
}
.p4-bottom button{
width: 40%;
}
.p5 {
position: relative;
}
.p5 .back {
position: absolute;
right: 10%;
bottom: 2vw;
font-size: 2vw;
border: 1px solid black;
padding: 0.5vw;
}
/* 全屏 */
::backdrop {
z-index:0;
background-color: white !important;
}
html, *:fullscreen, *:-webkit-full-screen, *:-moz-full-screen {
background-color: white !important;
z-index:1;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="zh_CN" >
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="img/favicon.ico" type="image/x-icon">
<title>番茄钟</title>
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<script src="js/NoSleep.min.js"></script>
<script src="js/jquery-3.6.0.min.js"></script>
<script src="js/echarts.min.js"></script>
<script src="js/tools.js"></script>
<script src="js/index.js"></script>
<!-- 版心 -->
<div class="w">
<!-- 帮助界面 -->
<div class="class page p0 ">
<h3>提示(点击进入主页)</h3>
<div class="tip">
<p>点击屏幕中间进入/退出全屏</p>
<p>点击左上角的番茄钟获取提示</p>
<p>点击中上角的时间电量切换钟表模式</p>
<p>点击右上角的已工作时间切换显示计划时间</p>
</div>
</div>
<!-- 第一页:主界面 -->
<div class="page p1 none">
<div class="top flex">
<!-- 顶部状态栏 -->
<div class="top-left iconfont">&#xe602;番茄钟</div>
<div class="top-center"><span class="now-time">00:00:00</span></div>
<div class="top-right">好好学习,天天向上</div>
</div>
<div class="main flex ">
<!-- 主要的时钟部分 -->
<div class="lcd" id="n1">4</div>
<div class="lcd" id="n2">5</div>
<div class="lcd" id="n3">:</div>
<div class="lcd" id="n4">0</div>
<div class="lcd" id="n5">0</div>
</div>
<div class="footer flex">
<!-- 底部 -->
<div class="footer-left play iconfont">&#xe769;</div>
<div class="footer-center reset iconfont">&#xe60a;</div>
<div class="footer-right setting iconfont">&#xe631;</div>
</div>
</div>
<!-- 第二页:设置界面 -->
<div class="page p2 none">
<ol>
<li class="option">设置工作/休息时间</li>
<li class="option">设置计划倒计时</li>
<li class="option">查看工作历史</li>
<li class="option">清除本地数据</li>
<li class="option">返回</li>
</ol>
</div>
<!-- 第三页:设置工作/休息时间 -->
<div class="page p3 none">
<div class="p3-main">
<div class="line1">
<span>工作时间(分):</span>
<span><input type="text" name="study-time" id="input-study-time" value="45"></span>
</div>
<div class="line2">
<span>休息时间(分):</span>
<span ><input type="text" name="rest-time" id="input-rest-time" value="5"></span>
</div>
</div>
<div class="p3-bottom">
<button>应用</button>
<button>返回</button>
</div>
</div>
<!-- 第四页:设置计划倒计时 -->
<div class="page p4 none">
<div class="p4-main">
<div class="line1">
<span>计划名称:</span>
<span><input type="text" name="plan-name" id="input-plan-name" value=""></span>
</div>
<div class="line2">
<span>截止日期:</span>
<span ><input type="text" name="plan-time" id="input-plan-time" value="2021/12/31"></span>
</div>
</div>
<div class="p4-bottom">
<button>应用</button>
<button>返回</button>
</div>
</div>
<!-- 第五页:展示历史工作时间 -->
<div class="page p5 none ">
<div id="echarts" style="width:85vw;height: 50vw; margin: 0 auto;">
</div>
</div>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,182 @@
// 入口函数
$(()=>{
let myChart ;
let isFullScreen = false;
//屏幕常量!
let noSleep = new NoSleep();
let playSound = (src) => {
au = document.createElement("audio");
au.preload="auto";
au.src = '/admin/tools/clock/audio/' + src + '.mp3';
au.play();
}
//绑定事件
// 全屏
$(".main").on("click",()=>{
if(isFullScreen){
document.exitFullscreen();
}
else{
document.body.requestFullscreen();
}
isFullScreen = !isFullScreen;
});
//绑定提示取消
$(".p0").on("click",()=>{
$(".page").hide();
$(".p1").show();
noSleep.enable();
});
$(".top-left").on("click",()=>{
$(".page").hide();
$(".p0").show();
});
//切换工作时间/计划倒计时
$(".top-right").on("click",()=>{
isShowPlan = !isShowPlan;
});
//切换时钟
$(".top-center").on("click",()=>{
isClock = !isClock;
showNowTime();
});
//播放按钮
$(".play").on("click",()=>{
//如果没播放并且在休息,按两下直接开始工作
if (!isPlay && isRest){
res_time = t_study;
isRest = false;
}
if (!isPlay){
$(".play").html(icon_stop);
playSound('tick');
}
if (isPlay){
$(".play").html(icon_play);
}
isPlay = !isPlay;
});
//重置时间
$(".reset").on("click",()=>{
if (isRest){
res_time = t_rest ;
}
else {
res_time = t_study ;
}
showRestTime();
});
//设置
$(".setting").on("click",()=>{
$(".page").hide();
$(".p2").show();
});
//设置工作/休息时间
$(".p2 .option").eq(0).on("click",()=>{
$(".page").hide();
$(".p3").show();
});
//设置计划倒计时
$(".p2 .option").eq(1).on("click",()=>{
$(".page").hide();
$(".p4").show();
});
//查看工作历史
$(".p2 .option").eq(2).on("click",()=>{
$(".page").hide();
$(".p5").show();
myChart = showEcharts();
});
//清除本地数据
$(".p2 .option").eq(3).on("click",()=>{
window.localStorage.clear();
localData = [];
alert("已清除!");
});
//返回
$(".p2 .option").eq(4).on("click",()=>{
$(".page").hide();
$(".p1").show();
});
//第三页设置工作/休息时间
$(".p3-bottom button").eq(0).on("click",()=>{
t_study = $(".p3 input").eq(0).val() * 60;
t_rest = $(".p3 input").eq(1).val() * 60;
if(isRest){
res_time = t_rest;
}
else {
res_time = t_study;
}
showRestTime();
saveSetting();
// alert("完成!");
// console.log(t_study);
});
$(".p3-bottom button").eq(1).on("click",()=>{
$(".page").hide();
$(".p2").show();
});
//第四页 设置计划倒计时
$(".p4-bottom button").eq(0).on("click",()=>{
plan_name = $(".p4 input").eq(0).val();
plan_time = $(".p4 input").eq(1).val() + " " + "08:00:00";
if(isShowPlan){
showPlan();
}
saveSetting();
// alert("完成!");
// console.log(plan_name);
});
$(".p4-bottom button").eq(1).on("click",()=>{
$(".page").hide();
$(".p2").show();
});
//第五页返回
$(".p5").on("click",()=>{
$(".page").hide();
$(".p2").show();
myChart.clear();
});
//开始初始化,设置计时器
loadLocalData();
setInterval(()=>{
showTopBar();
//如果倒计时归零
if(!res_time){
isRest = !isRest;
//该休息了
if(isRest){
$(".top-left").html(icon_rest+"休息中");
playSound('rest');
res_time = t_rest;
}
//该工作了
else{
$(".top-left").html(icon_study+"工作中");
playSound('work');
res_time = t_study;
}
}
//如果在播放
if (isPlay && !isClock){
if(!isRest){
$(".top-left").html(icon_study+"工作中");
study_time++;
}
res_time--;
saveStudyTime();
showRestTime();
}
},1000);
});

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,304 @@
// 初始化变量和常量
const icon_play = "&#xe769;";
const icon_stop = "&#xe637;";
const icon_study = "&#xe61e;";
const icon_rest = "&#xe623;";
let t_study = 45*60;
let t_rest = 5*60;
let plan_name = "godo发布";
let plan_time = "2021/12/31 18:00:00";
let isPlay = false;
let isRest = false;
let isShowPlan = false;
let isClock = false;
let localData = {};
let today = null;
let res_time = t_study;
let study_time = 0;
//获取现在的日期并返回
function getToday(){
let t = new Date();
let day = t.getDate();
let month = t.getMonth() +1 ;
let year = t.getFullYear();
let tmp = year +
(month < 10 ? "0" + month : month) +
(day < 10 ? "0" + day : day);
return tmp;
}
//更新today变量为最新的当前日期
function updateToday(){
today = getToday();
}
//读取本地的存储放到localDate对象中
function loadLocalData(){
updateToday();
//如果存在本地数据
if(window.localStorage && window.localStorage.length){
for (let i=0;i<window.localStorage.length;i++){
let date = window.localStorage.key(i);
let data = window.localStorage.getItem(date);
localData[date] = data;
}
//console.log('本地数据读取完成:'+JSON.stringify(localData));
//如果有今天的数据,那今天的工作时间叠加
if(localData[today]){
study_time+=localData[today];
}
if(localData['plan-name']){
plan_name = localData['plan-name'];
$(".p4 input").eq(0).val(plan_name);
}
if(localData['plan-time']){
plan_time = localData['plan-time'];
$(".p4 input").eq(1).val(plan_time.substr(0,10));
}
if(localData['t_rest']){
t_rest = localData['t_rest'];
$(".p3 input").eq(1).val(t_rest/60);
}
if(localData['t_study']){
t_study = localData['t_study'];
$(".p3 input").eq(0).val(t_study/60);
res_time = t_study;
showRestTime();
}
}
else {
study_time = 0;
}
}
//把剩余的秒数计算成四个要显示的数字并返回
function calTime(t){
let min = Math.floor(t/60);
let sec = t %60;
return [Math.floor(min/10) , min%10 , Math.floor(sec/10) , sec %10];
}
//更新计数器时间
function showRestTime(){
let arr = calTime(res_time);
$("#n1").text(arr[0]);
if(arr[0]==1){
$("#n1").css("transform","translate(30%)");
}
else {
$("#n1").css("transform","none");
}
$("#n2").text(arr[1]);
if(arr[1]==1){
$("#n2").css("transform","translate(30%)");
}
else {
$("#n2").css("transform","none");
}
$("#n4").text(arr[2]);
if(arr[2]==1){
$("#n4").css("transform","translate(30%)");
}
else {
$("#n4").css("transform","none");
}
$("#n5").text(arr[3]);
if(arr[3]==1){
$("#n5").css("transform","translate(30%)");
}
else {
$("#n5").css("transform","none");
}
}
//更新现在的时间和电量
function showNowTime(){
let t = new Date();
let h = t.getHours();
h = h < 10 ? "0" + h : h;
let m = t.getMinutes();
m = m < 10 ? "0" + m : m;
let s = t.getSeconds();
s = s < 10 ? "0" + s : s;
if(navigator.getBattery){
navigator.getBattery().then((result)=>{
$(".now-time").text(h + ":" + m + ":" + s +" 电量:"+parseInt(result.level*100)+"%") ;
})
}
else {
$(".now-time").text(h + ":" + m + ":" + s);
}
//是否开启钟表模式
if(isClock){
//console.log(h+m);
let arr = [h.toString()[0],h.toString()[1],m.toString()[0],m.toString()[1]]
$("#n1").text(arr[0]);
$("#n2").text(arr[1]);
$("#n4").text(arr[2]);
$("#n5").text(arr[3]);
if(arr[0]==1){
$("#n1").css("transform","translate(30%)");
}
else {
$("#n1").css("transform","none");
}
$("#n2").text(arr[1]);
if(arr[1]==1){
$("#n2").css("transform","translate(30%)");
}
else {
$("#n2").css("transform","none");
}
$("#n4").text(arr[2]);
if(arr[2]==1){
$("#n4").css("transform","translate(30%)");
}
else {
$("#n4").css("transform","none");
}
$("#n5").text(arr[3]);
if(arr[3]==1){
$("#n5").css("transform","translate(30%)");
}
else {
$("#n5").css("transform","none");
}
}
else{
showRestTime();
}
}
//更新计划倒计时
function showPlan(){
let t = new Date();
let t0 = new Date(plan_time);
let ss = t0.getTime() - t.getTime();
let r_plan_days = parseInt(ss / 1000/60/60/24);
let r_plan_hours = parseInt((ss / 1000/60/60)%24);
$(".top-right").text("距" + plan_name+"还有:" + r_plan_days + "天" + r_plan_hours + "小时");
}
//存储工作时间
function saveStudyTime(){
console.log('存储数据');
if (getToday()!==today){
study_time = 0 ;
updateToday();
}
localData[today]=study_time.toFixed(2);
window.localStorage.setItem(today,study_time);
}
//更新今天已工作时间
function showStudyTime(){
$(".top-right").text("今天已工作:" +
String(Math.floor(study_time / 60)) +
"分钟" +
String(study_time % 60) +
"秒");
}
//更新最上面一栏
function showTopBar(){
showNowTime();
if(isShowPlan){
showPlan();
}
else{
showStudyTime();
}
}
//保存数据
function saveSetting (){
localData['plan-name'] = plan_name;
localData['plan-time'] = plan_name;
localData['t_study'] = t_study;
localData['t_rest'] = t_rest;
window.localStorage.setItem('plan-name',plan_name);
window.localStorage.setItem('plan-time',plan_time);
window.localStorage.setItem('t_study',t_study);
window.localStorage.setItem('t_rest',t_rest);
}
//展示图表
function showEcharts(){
//console.log("缓存数据:"+ window.localStorage);
//console.log('localData'+localData);
let myChart = echarts.init(document.getElementById('echarts'));
let dateArr = [];
let timeArr = [];
let re = /^20[0-9]{2}/;
for (let i in localData){
if(re.test(i)){
timeArr.push((localData[i]/60).toFixed(2));
//处理原来的数据
let re1 = /^0/;
if (re1.test(i.substr(4,4))){
dateArr.push(i[5]+'/'+i[6]+i[7]);
}
}
}
//处理数据补0
if (dateArr.length<4){
let n = 4 - dateArr.length;
if (dateArr.length==0){
let t = new Date();
t = t.getMonth()+1 + '/'+t.getDate();
dateArr.push(t);
timeArr.push(0);
}
for (let j=0;j<n;j++){
let t = new Date();
t=t.setDate(t.getDate()+j+1);
t = new Date(t);
dateArr.push(t.getMonth()+1+'/'+t.getDate());
timeArr.push(0);
}
}
// console.log(dateArr);
// console.log(timeArr);
let option = {
title: {
text: '工作记录',
subtext: '任意点击返回',
subtextStyle: {
align: 'right'
},
left: 'center',
icon : 'none',
textStyle: {
fontSize: 26
},
},
xAxis: {
data: dateArr,
name: '日期'
},
yAxis: {
name: '工作时间/分钟',
max: function(value){
return value.max<30?30:value.max;
}
},
series: [{
name: '时间(分)',
type: 'bar',
data: timeArr,
barMaxWidth: '40%',
itemStyle:{
color: '#555',
normal: {
label: {
show: true,
position: 'top',
formatter: '{c}分钟'
}
}
}
}]
};
myChart.setOption(option);
return myChart;
}

18
plugins/clock/info.json Normal file
View file

@ -0,0 +1,18 @@
{
"name": "todo",
"version": "0.0.1",
"description": "todo worker",
"main": "frontend/index.html",
"icon":"",
"position":["desktop","magnet"],
"width":800,
"height":600,
"usetype":["person","member","compony"],
"category":["work","life","productivity"],
"releases":[""],
"platform": "any",
"arch": "any",
"author": "aiok",
"license": "MIT",
"dependencies": {}
}

View file

@ -0,0 +1,10 @@
{
"name" : "godoos",
"version" : "0.0.1",
"description" : "godoos",
"main" : "frontend",
"platform" : ["windows","darwin","linux"],
"author" : "aiok",
"license" : "MIT",
"dependencies" : {}
}