11 Commits

Author SHA1 Message Date
Harry-zklcdc 7fc2c3ec09 [Fix] 🐛 Send Instance Current Config to Controler #23 2025-05-05 21:26:47 +08:00
Harry-zklcdc 011827f5c7 [Fix] 🐛 PreCheck the Config if Instance has Changed #21 2025-03-30 01:25:21 +08:00
Harry-zklcdc 69554b5e39 [Fix] 🐛 Ignore GpuCount Value at Instance Modify to CpuOnly #21 2025-03-30 01:24:12 +08:00
Harry-zklcdc a24fb8e8ad [Feat] Add Auto Dark Mode 2025-03-17 13:39:20 +08:00
Harry-zklcdc 42de9caf52 [Feat] Add Docs Link 2025-03-06 00:26:00 +08:00
Harry-zklcdc 5780f84714 [Feat] Set logging model to "main" in main function 2025-03-05 18:59:42 +08:00
Harry-zklcdc de8c601be9 [Fix] 🐛 Instance Mount Path 2025-03-04 18:21:57 +08:00
Harry-zklcdc e283a626a1 [Fix] 🐛 Photo Show Error 2025-02-28 21:26:33 +08:00
Harry-zklcdc 37ba1baf71 [Fix] 😅 Typo #15 2025-02-25 00:02:57 +08:00
Harry-zklcdc 81b52e80b5 [Fix] 🐛 Fix Regexp Pattern to Support subdomain Email #15 2025-02-24 23:38:04 +08:00
Harry-zklcdc 13bd350274 [Fix] 🐛 Add Sendding Status #15 2025-02-24 22:12:25 +08:00
14 changed files with 102 additions and 38 deletions
+2 -2
View File
@@ -46,7 +46,7 @@
> [!WARNING] > [!WARNING]
> 部署仓库: [XShengTech/MEGREZ-Deploy](https://github.com/XShengTech/MEGREZ-Deploy) > 部署仓库: [XShengTech/MEGREZ-Deploy](https://github.com/XShengTech/MEGREZ-Deploy)
> >
> 查看文档 [**>>> 🚧 正在施工中 <<<**]() > 查看文档 [**>>> MEGREZ 文档 <<<**](http://docs.megrez.xsheng-ai.com/)
## 📌 效果展示 ## 📌 效果展示
@@ -81,7 +81,7 @@
| VSCode 网页版 | Jupyter Notebook | Grafana 资源监控 | | VSCode 网页版 | Jupyter Notebook | Grafana 资源监控 |
| ---------------------------------------------------- | ----------------------------------------------- | -------------------------------- | | ---------------------------------------------------- | ----------------------------------------------- | -------------------------------- |
| ![image-20250116010708107](./assets/code-server/.png) | ![Jupyter NoteBook](./assets/juper-notebook.png) | ![Grafana](./assets/grafana.png) | | ![image-20250116010708107](./assets/code-server.png) | ![Jupyter NoteBook](./assets/juper-notebook.png) | ![Grafana](./assets/grafana.png) |
### 系统管理 ### 系统管理
+1 -7
View File
@@ -56,15 +56,9 @@ const model = ref([
{ {
label: '使用文档', label: '使用文档',
icon: 'pi pi-fw pi-book text-amber-500', icon: 'pi pi-fw pi-book text-amber-500',
url: '#', url: 'http://docs.megrez.xsheng-ai.com/guide/usage/',
target: '_blank' target: '_blank'
}, },
// {
// label: '开源信息',
// icon: 'pi pi-fw pi-cog',
// url: 'https://github.com/primefaces/sakai-vue',
// target: '_blank'
// },
] ]
} }
]); ]);
+27 -16
View File
@@ -4,10 +4,36 @@ const layoutConfig = reactive({
preset: 'Aura', preset: 'Aura',
primary: 'blue', primary: 'blue',
surface: null, surface: null,
darkTheme: false, darkTheme: window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches,
menuMode: 'static' menuMode: 'static'
}); });
// Listen for changes to the prefers-color-scheme media query
if (window.matchMedia) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
layoutConfig.darkTheme = e.matches;
toggleDarkMode();
});
}
const toggleDarkMode = () => {
if (!document.startViewTransition) {
executeDarkModeToggle();
return;
}
document.startViewTransition(() => executeDarkModeToggle(event));
};
if (layoutConfig.darkTheme) {
toggleDarkMode()
}
const executeDarkModeToggle = () => {
layoutConfig.darkTheme = !layoutConfig.darkTheme;
document.documentElement.classList.toggle('app-dark');
};
const layoutState = reactive({ const layoutState = reactive({
staticMenuDesktopInactive: false, staticMenuDesktopInactive: false,
overlayMenuActive: false, overlayMenuActive: false,
@@ -39,21 +65,6 @@ export function useLayout() {
layoutConfig.menuMode = mode; layoutConfig.menuMode = mode;
}; };
const toggleDarkMode = () => {
if (!document.startViewTransition) {
executeDarkModeToggle();
return;
}
document.startViewTransition(() => executeDarkModeToggle(event));
};
const executeDarkModeToggle = () => {
layoutConfig.darkTheme = !layoutConfig.darkTheme;
document.documentElement.classList.toggle('app-dark');
};
const onMenuToggle = () => { const onMenuToggle = () => {
if (layoutConfig.menuMode === 'overlay') { if (layoutConfig.menuMode === 'overlay') {
layoutState.overlayMenuActive = !layoutState.overlayMenuActive; layoutState.overlayMenuActive = !layoutState.overlayMenuActive;
+6 -1
View File
@@ -20,7 +20,8 @@
<span class="font-medium no-underline ml-2 text-right cursor-pointer text-slate-600">记起密码<span <span class="font-medium no-underline ml-2 text-right cursor-pointer text-slate-600">记起密码<span
class="text-primary" @click="handleLogin">立即登入</span></span> class="text-primary" @click="handleLogin">立即登入</span></span>
</div> </div>
<Button label="发送邮件" class="w-full" @click="handleSubmit"></Button> <Button v-if="!requesting" label="发送邮件" class="w-full" @click="handleSubmit"></Button>
<Button v-else label="发送中" icon="pi pi-spin pi-spinner" disabled class="w-full"></Button>
</div> </div>
</div> </div>
</div> </div>
@@ -40,16 +41,20 @@ import { useRouter } from 'vue-router';
const router = useRouter() const router = useRouter()
const toast = useToast() const toast = useToast()
const requesting = ref(false)
const form = ref({ const form = ref({
email: '' email: ''
}) })
const handleSubmit = () => { const handleSubmit = () => {
requesting.value = true
api.UserForgetRequest(form.value).then(res => { api.UserForgetRequest(form.value).then(res => {
toast.add({ severity: 'success', summary: '发送成功', detail: '请查看邮箱', life: 3000 }) toast.add({ severity: 'success', summary: '发送成功', detail: '请查看邮箱', life: 3000 })
requesting.value = false
}).catch(err => { }).catch(err => {
console.error(err) console.error(err)
toast.add({ severity: 'error', summary: '发送失败', detail: '请检查后重新尝试', life: 3000 }) toast.add({ severity: 'error', summary: '发送失败', detail: '请检查后重新尝试', life: 3000 })
requesting.value = false
}) })
} }
+1 -1
View File
@@ -27,7 +27,7 @@
<span class="font-medium no-underline ml-2 text-right cursor-pointer text-slate-600">记起密码<span <span class="font-medium no-underline ml-2 text-right cursor-pointer text-slate-600">记起密码<span
class="text-primary" @click="handleLogin">立即登入</span></span> class="text-primary" @click="handleLogin">立即登入</span></span>
</div> </div>
<Button label="注册" class="w-full" @click="handleSubmit"></Button> <Button label="重置密码" class="w-full" @click="handleSubmit"></Button>
</div> </div>
</div> </div>
</div> </div>
+3
View File
@@ -498,6 +498,9 @@ const instanceModify = async () => {
setTimeout(() => { setTimeout(() => {
getInstances() getInstances()
}, 100); }, 100);
if (instanceConfiguration.value.cpu_only) {
delete instanceConfiguration.value.gpu_count
}
await api.AdminInstancesModify(instanceDetail.value.id, instanceConfiguration.value).then(async (res) => { await api.AdminInstancesModify(instanceDetail.value.id, instanceConfiguration.value).then(async (res) => {
toast.add({ severity: 'success', summary: '调整配置', detail: '已调整配置', life: 3000 }); toast.add({ severity: 'success', summary: '调整配置', detail: '已调整配置', life: 3000 });
instanceModifyVisible.value = false instanceModifyVisible.value = false
@@ -501,6 +501,9 @@ const instanceModify = async () => {
setTimeout(() => { setTimeout(() => {
getInstances() getInstances()
}, 100); }, 100);
if (instanceConfiguration.value.cpu_only) {
delete instanceConfiguration.value.gpu_count
}
await api.UserInstancesModify(instanceDetail.value.id, instanceConfiguration.value).then(async (res) => { await api.UserInstancesModify(instanceDetail.value.id, instanceConfiguration.value).then(async (res) => {
toast.add({ severity: 'success', summary: '调整配置', detail: '已调整配置', life: 3000 }); toast.add({ severity: 'success', summary: '调整配置', detail: '已调整配置', life: 3000 });
instanceModifyVisible.value = false instanceModifyVisible.value = false
+1 -1
View File
@@ -5,7 +5,7 @@ import (
) )
func EmailFormat(email string) bool { func EmailFormat(email string) bool {
pattern := `^\w+(-+.\w+)*@\w+(-.\w+)*.\w+(-.\w+)*$` pattern := `\w[-\w.+]*@([-A-Za-z0-9]+\.)+[A-Za-z]{2,14}`
match, err := regexp.MatchString(pattern, email) match, err := regexp.MatchString(pattern, email)
if err != nil { if err != nil {
return false return false
+1
View File
@@ -27,6 +27,7 @@ var (
) )
func main() { func main() {
l.SetModel("main")
l.Info("Branch: %s", BRANCH) l.Info("Branch: %s", BRANCH)
l.Info("Version: %s", VERSION) l.Info("Version: %s", VERSION)
l.Info("Commit: %s", COMMIT) l.Info("Commit: %s", COMMIT)
+15 -1
View File
@@ -33,6 +33,10 @@ func modifyHandler(ctx iris.Context) {
return return
} }
if req.CpuOnly {
req.GpuCount = nil
}
if req.GpuCount != nil { if req.GpuCount != nil {
if *req.GpuCount < 0 { if *req.GpuCount < 0 {
middleware.Error(ctx, middleware.CodeBadRequest, iris.StatusBadRequest) middleware.Error(ctx, middleware.CodeBadRequest, iris.StatusBadRequest)
@@ -62,7 +66,17 @@ func modifyHandler(ctx iris.Context) {
return return
} }
if req.CpuOnly == instance.CpuOnly && req.CpuOnly { hasChanges := false
if req.CpuOnly != instance.CpuOnly {
hasChanges = true
}
if req.GpuCount != nil && *req.GpuCount != instance.GpuCount {
hasChanges = true
}
if req.VolumeSize != nil && *req.VolumeSize != instance.VolumeSize {
hasChanges = true
}
if !hasChanges {
middleware.Error(ctx, middleware.CodeBadRequest, iris.StatusBadRequest) middleware.Error(ctx, middleware.CodeBadRequest, iris.StatusBadRequest)
return return
} }
+15 -1
View File
@@ -39,6 +39,10 @@ func modifyHandler(ctx iris.Context) {
return return
} }
if req.CpuOnly {
req.GpuCount = nil
}
if req.GpuCount != nil { if req.GpuCount != nil {
if *req.GpuCount < 0 { if *req.GpuCount < 0 {
middleware.Error(ctx, middleware.CodeBadRequest, iris.StatusBadRequest) middleware.Error(ctx, middleware.CodeBadRequest, iris.StatusBadRequest)
@@ -68,7 +72,17 @@ func modifyHandler(ctx iris.Context) {
return return
} }
if req.CpuOnly == instance.CpuOnly && req.CpuOnly { hasChanges := false
if req.CpuOnly != instance.CpuOnly {
hasChanges = true
}
if req.GpuCount != nil && *req.GpuCount != instance.GpuCount {
hasChanges = true
}
if req.VolumeSize != nil && *req.VolumeSize != instance.VolumeSize {
hasChanges = true
}
if !hasChanges {
middleware.Error(ctx, middleware.CodeBadRequest, iris.StatusBadRequest) middleware.Error(ctx, middleware.CodeBadRequest, iris.StatusBadRequest)
return return
} }
+13 -3
View File
@@ -43,9 +43,19 @@ func modify(serverID uint, data Data) (err error) {
return errors.New("instance status error") return errors.New("instance status error")
} }
if data.CpuOnly == instance.CpuOnly && data.CpuOnly { hasChanges := false
lc.Error("instance already cpu_only mode") if data.CpuOnly != instance.CpuOnly {
return errors.New("instance already cpu_only mode") hasChanges = true
}
if data.GpuCount != nil && *data.GpuCount != instance.GpuCount {
hasChanges = true
}
if data.VolumeSize != nil && *data.VolumeSize != instance.VolumeSize {
hasChanges = true
}
if !hasChanges {
lc.Error("no changes")
return errors.New("no changes")
} }
oldVolumeSize := instance.VolumeSize oldVolumeSize := instance.VolumeSize
@@ -62,7 +62,7 @@ func createInstance(ip string, port int, apikey string,
if config.GetSystemMountDir() != "" { if config.GetSystemMountDir() != "" {
data.Binds = append(data.Binds, bindStruct{ data.Binds = append(data.Binds, bindStruct{
Src: config.GetSystemMountDir(), Src: config.GetSystemMountDir(),
Dest: "/root/megrez-pub", Dest: "/root/megrez-mnt",
}) })
} }
+9
View File
@@ -45,6 +45,15 @@ func patchGpu(ip string, port int, apikey string,
Dest: "/root/megrez-tmp", Dest: "/root/megrez-tmp",
}, },
} }
data.GpuPatch = &gpuPatchStruct{
GpuCount: gpuCount,
}
data.CpuPatch = &cpuPatchStruct{
CpuCount: cpuCountPerGpu * gpuCount,
}
data.MemoryPatch = &MemoryPatchStruct{
Memory: strconv.Itoa(memoryPerGpu*gpuCount) + "GB",
}
} }
reqBytes, err := json.Marshal(data) reqBytes, err := json.Marshal(data)