diff --git a/apps/server-web/package.json b/apps/server-web/package.json index 626d9bcd5..2c9e42f39 100644 --- a/apps/server-web/package.json +++ b/apps/server-web/package.json @@ -51,7 +51,8 @@ "i18next-resources-to-backend": "^1.2.1", "react-i18next": "^14.1.0", "@radix-ui/react-switch": "^1.1.0", - "classnames": "^2.5.1" + "classnames": "^2.5.1", + "fast-glob": "^3.3.2" }, "devDependencies": { "electron": "28.1.0", diff --git a/apps/server-web/src/locales/i18n/bg/translation.json b/apps/server-web/src/locales/i18n/bg/translation.json index 1ca895f37..22c0e2529 100644 --- a/apps/server-web/src/locales/i18n/bg/translation.json +++ b/apps/server-web/src/locales/i18n/bg/translation.json @@ -63,7 +63,8 @@ "INFO": "Информация", "UPDATE_AVAILABLE": "Налична е нова актуализация! Моля, щракнете върху бутона Изтегляне сега по-долу.", "EXIT_MESSAGE": "Мрежата на сървъра все още работи, сигурни ли сте, че ще излезете от приложението?", - "UPDATE_SUCCESS": "Актуализирайте успешно" + "UPDATE_SUCCESS": "Актуализирайте успешно", + "SERVER_RUN_DIALOG": "Сървърната мрежа работи в момента, искате ли да рестартирате сървъра?" }, "LANGUAGES": { "en": "Английски", diff --git a/apps/server-web/src/locales/i18n/en/translation.json b/apps/server-web/src/locales/i18n/en/translation.json index 91ba9f7d5..1d506ae0e 100644 --- a/apps/server-web/src/locales/i18n/en/translation.json +++ b/apps/server-web/src/locales/i18n/en/translation.json @@ -63,7 +63,8 @@ "INFO": "Info", "UPDATE_AVAILABLE": "New Update is available! Please click button Download Now below.", "EXIT_MESSAGE": "Server web still running, Are you sure to exit the app ?", - "UPDATE_SUCCESS": "Update Successfully" + "UPDATE_SUCCESS": "Update Successfully", + "SERVER_RUN_DIALOG": "Server web currently running, You want to restart the server ?" }, "LANGUAGES": { "en": "English", diff --git a/apps/server-web/src/main/helpers/constant.ts b/apps/server-web/src/main/helpers/constant.ts index 2885630d1..89889dd37 100644 --- a/apps/server-web/src/main/helpers/constant.ts +++ b/apps/server-web/src/main/helpers/constant.ts @@ -14,7 +14,8 @@ export const EventLists = { UPDATE_CANCELLED: 'UPDATE_CANCELLED', CHANGE_LANGUAGE: 'CHANGE_LANGUAGE', OPEN_WEB: 'OPEN_WEB', - SERVER_WINDOW: 'SERVER_WINDOW' + SERVER_WINDOW: 'SERVER_WINDOW', + RESTART_SERVER: 'RESTART_SERVER' } export const SettingPageTypeMessage = { @@ -33,7 +34,8 @@ export const SettingPageTypeMessage = { langChange: 'lang', updateSetting: 'update-setting', updateSettingResponse: 'update-setting-response', - updateCancel: 'update-cancel' + updateCancel: 'update-cancel', + restartServer: 'restart-server' } export const ServerPageTypeMessage = { diff --git a/apps/server-web/src/main/helpers/index.ts b/apps/server-web/src/main/helpers/index.ts index e1b9aad0a..f1642c4ed 100644 --- a/apps/server-web/src/main/helpers/index.ts +++ b/apps/server-web/src/main/helpers/index.ts @@ -1 +1,2 @@ -export * from './create-window' +export * from './create-window'; +export * from './replace-config'; diff --git a/apps/server-web/src/main/helpers/replace-config.ts b/apps/server-web/src/main/helpers/replace-config.ts new file mode 100644 index 000000000..98e12f9af --- /dev/null +++ b/apps/server-web/src/main/helpers/replace-config.ts @@ -0,0 +1,44 @@ +import fs from 'fs'; +import path from 'path'; +import fg from 'fast-glob'; +import os from 'os'; + +type EnvOptions = { + before: { + NEXT_PUBLIC_GAUZY_API_SERVER_URL?: string + }, + after: { + NEXT_PUBLIC_GAUZY_API_SERVER_URL?: string + } +} + +const scanAllFiles = async (files: string[], oldConfig: string, newConfig: string) => { + files.forEach((file) => { + if (path.extname(file) === '.js') { + try { + const data = fs.readFileSync(file, 'utf-8'); + const result = data.replace(new RegExp(oldConfig, 'g'), newConfig); + fs.writeFileSync(file, result, 'utf8'); + } catch (error) { + console.log('error replace', error); + } + } + }); +} +export const replaceConfig = async (folderPath: string, envOptions: EnvOptions) => { + try { + console.log('all files path', folderPath); + if (os.platform() === 'win32') { + folderPath = folderPath.replace(/\\/g, '/'); + } + console.log('final path', folderPath); + const NEXT_PUBLIC_GAUZY_API_SERVER_URL_BEFORE = `"NEXT_PUBLIC_GAUZY_API_SERVER_URL","${envOptions.before.NEXT_PUBLIC_GAUZY_API_SERVER_URL}"`; + const NEXT_PUBLIC_GAUZY_API_SERVER_URL_AFTER = `"NEXT_PUBLIC_GAUZY_API_SERVER_URL","${envOptions.after.NEXT_PUBLIC_GAUZY_API_SERVER_URL}"`; + const NEXT_PUBLIC_GAUZY_API_SERVER_URL_DEFAULT = `"NEXT_PUBLIC_GAUZY_API_SERVER_URL","https://api.ever.team"`; + const files = await fg(`${folderPath}/**/*`, { onlyFiles: true }); + await scanAllFiles(files, NEXT_PUBLIC_GAUZY_API_SERVER_URL_BEFORE, NEXT_PUBLIC_GAUZY_API_SERVER_URL_AFTER); + await scanAllFiles(files, NEXT_PUBLIC_GAUZY_API_SERVER_URL_DEFAULT, NEXT_PUBLIC_GAUZY_API_SERVER_URL_AFTER); + } catch (error) { + console.log('error on replacing file', error); + } +} diff --git a/apps/server-web/src/main/main.ts b/apps/server-web/src/main/main.ts index ebefb4b77..c0249ea0a 100644 --- a/apps/server-web/src/main/main.ts +++ b/apps/server-web/src/main/main.ts @@ -11,7 +11,11 @@ import { mainBindings } from 'i18next-electron-fs-backend'; import i18nextMainBackend from '../configs/i18n.mainconfig'; import fs from 'fs'; import { WebServer } from './helpers/interfaces'; +import { replaceConfig } from './helpers'; import Log from 'electron-log'; +import MenuBuilder from './menu'; + + console.log = Log.log; Object.assign(console, Log.functions); @@ -32,8 +36,10 @@ let intervalUpdate: NodeJS.Timeout; let tray: Tray; let settingWindow: BrowserWindow | null = null; let logWindow: BrowserWindow | null = null; +let SettingMenu: any = null; +let ServerWindowMenu: any = null; -Log.hooks.push((message:any, transport) => { +Log.hooks.push((message: any, transport) => { if (transport !== Log.transports.file) { return message; } @@ -167,7 +173,12 @@ const createWindow = async (type: 'SETTING_WINDOW' | 'LOG_WINDOW') => { mainBindings(ipcMain, settingWindow, fs); settingWindow.on('closed', () => { settingWindow = null; + SettingMenu = null }); + if (!SettingMenu) { + SettingMenu = new MenuBuilder(settingWindow); + } + SettingMenu.buildMenu(); break; case 'LOG_WINDOW': logWindow = new BrowserWindow(defaultOptionWindow); @@ -176,7 +187,12 @@ const createWindow = async (type: 'SETTING_WINDOW' | 'LOG_WINDOW') => { mainBindings(ipcMain, logWindow, fs); logWindow.on('closed', () => { logWindow = null; + ServerWindowMenu = null }) + if (!ServerWindowMenu) { + ServerWindowMenu = new MenuBuilder(logWindow); + } + ServerWindowMenu.buildMenu(); break; default: break; @@ -207,6 +223,16 @@ const stopServer = async () => { await desktopServer.stop(); }; +const restartServer = async () => { + await desktopServer.stop(); + const waitingForServerStop = setInterval(async () => { + if (!isServerRun) { + clearInterval(waitingForServerStop); + await runServer() + } + }, 1000) +} + const getEnvApi = () => { const setting: WebServer = LocalStore.getStore('config') return setting.server; @@ -237,15 +263,19 @@ const onInitApplication = () => { }) eventEmitter.on(EventLists.webServerStop, async () => { - isServerRun = false; await stopServer(); + isServerRun = false; + }) + + eventEmitter.on(EventLists.RESTART_SERVER, async () => { + await restartServer(); }) eventEmitter.on(EventLists.webServerStarted, () => { console.log(EventLists.webServerStarted) updateTrayMenu('SERVER_START', { enabled: false }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); updateTrayMenu('SERVER_STOP', { enabled: true }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); - updateTrayMenu('OPEN_WEB', { enabled: true}, eventEmitter, tray, trayMenuItems, i18nextMainBackend); + updateTrayMenu('OPEN_WEB', { enabled: true }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); updateTrayMenu('SERVER_STATUS', { label: 'MENU.SERVER_STATUS_STARTED' }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); if (logWindow) { logWindow.webContents.send(IPC_TYPES.SERVER_PAGE, { @@ -262,7 +292,7 @@ const onInitApplication = () => { console.log(EventLists.webServerStopped); updateTrayMenu('SERVER_STOP', { enabled: false }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); updateTrayMenu('SERVER_START', { enabled: true }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); - updateTrayMenu('OPEN_WEB', { enabled: false}, eventEmitter, tray, trayMenuItems, i18nextMainBackend); + updateTrayMenu('OPEN_WEB', { enabled: false }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); updateTrayMenu('SERVER_STATUS', { label: 'MENU.SERVER_STATUS_STOPPED' }, eventEmitter, tray, trayMenuItems, i18nextMainBackend); if (logWindow) { logWindow.webContents.send(IPC_TYPES.SERVER_PAGE, { @@ -385,14 +415,35 @@ ipcMain.on('message', async (event, arg) => { event.reply('message', `${arg} World!`) }) -ipcMain.on(IPC_TYPES.SETTING_PAGE, (event, arg) => { +ipcMain.on(IPC_TYPES.SETTING_PAGE, async (event, arg) => { console.log('main setting page', arg); switch (arg.type) { case SettingPageTypeMessage.saveSetting: + const existingConfig = getEnvApi(); LocalStore.updateConfigSetting({ server: arg.data }); - event.sender.send(IPC_TYPES.SETTING_PAGE, { type: SettingPageTypeMessage.mainResponse, data: true }); + const dirFiles = 'standalone/apps/web/.next'; + const devDirFilesPath = path.join(__dirname, resourceDir.webServer, dirFiles); + const packDirFilesPath = path.join(process.resourcesPath, 'release', 'app', 'dist', dirFiles) + const diFilesPath = isPack ? packDirFilesPath : devDirFilesPath; + await replaceConfig( + diFilesPath, + { + before: { + NEXT_PUBLIC_GAUZY_API_SERVER_URL: existingConfig?.NEXT_PUBLIC_GAUZY_API_SERVER_URL + }, + after: { + NEXT_PUBLIC_GAUZY_API_SERVER_URL: arg.data.NEXT_PUBLIC_GAUZY_API_SERVER_URL + } + } + ) + event.sender.send(IPC_TYPES.SETTING_PAGE, { + type: SettingPageTypeMessage.mainResponse, data: { + status: true, + isServerRun: isServerRun + } + }); break; case SettingPageTypeMessage.checkUpdate: updater.checkUpdate(); @@ -408,6 +459,9 @@ ipcMain.on(IPC_TYPES.SETTING_PAGE, (event, arg) => { event.sender.send('languageSignal', arg.data); eventEmitter.emit(EventLists.CHANGE_LANGUAGE, { code: arg.data }) break; + case SettingPageTypeMessage.restartServer: + eventEmitter.emit(EventLists.RESTART_SERVER) + break; default: break; } diff --git a/apps/server-web/src/main/menu.ts b/apps/server-web/src/main/menu.ts index ba0fb7709..7e788eef2 100644 --- a/apps/server-web/src/main/menu.ts +++ b/apps/server-web/src/main/menu.ts @@ -54,27 +54,13 @@ export default class MenuBuilder { buildDarwinTemplate(): MenuItemConstructorOptions[] { const subMenuAbout: DarwinMenuItemConstructorOptions = { - label: 'Electron', + label: app.getName(), submenu: [ { - label: 'About ElectronReact', + label: `About ${app.getName()}`, selector: 'orderFrontStandardAboutPanel:', }, { type: 'separator' }, - { label: 'Services', submenu: [] }, - { type: 'separator' }, - { - label: 'Hide ElectronReact', - accelerator: 'Command+H', - selector: 'hide:', - }, - { - label: 'Hide Others', - accelerator: 'Command+Shift+H', - selector: 'hideOtherApplications:', - }, - { label: 'Show All', selector: 'unhideAllApplications:' }, - { type: 'separator' }, { label: 'Quit', accelerator: 'Command+Q', @@ -84,22 +70,6 @@ export default class MenuBuilder { }, ], }; - const subMenuEdit: DarwinMenuItemConstructorOptions = { - label: 'Edit', - submenu: [ - { label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' }, - { label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' }, - { type: 'separator' }, - { label: 'Cut', accelerator: 'Command+X', selector: 'cut:' }, - { label: 'Copy', accelerator: 'Command+C', selector: 'copy:' }, - { label: 'Paste', accelerator: 'Command+V', selector: 'paste:' }, - { - label: 'Select All', - accelerator: 'Command+A', - selector: 'selectAll:', - }, - ], - }; const subMenuViewDev: MenuItemConstructorOptions = { label: 'View', submenu: [ @@ -110,13 +80,6 @@ export default class MenuBuilder { this.mainWindow.webContents.reload(); }, }, - { - label: 'Toggle Full Screen', - accelerator: 'Ctrl+Command+F', - click: () => { - this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); - }, - }, { label: 'Toggle Developer Tools', accelerator: 'Alt+Command+I', @@ -130,10 +93,17 @@ export default class MenuBuilder { label: 'View', submenu: [ { - label: 'Toggle Full Screen', - accelerator: 'Ctrl+Command+F', + label: 'Reload', + accelerator: 'Command+R', click: () => { - this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); + this.mainWindow.webContents.reload(); + }, + }, + { + label: 'Toggle Developer Tools', + accelerator: 'Alt+Command+I', + click: () => { + this.mainWindow.webContents.toggleDevTools(); }, }, ], @@ -148,7 +118,6 @@ export default class MenuBuilder { }, { label: 'Close', accelerator: 'Command+W', selector: 'performClose:' }, { type: 'separator' }, - { label: 'Bring All to Front', selector: 'arrangeInFront:' }, ], }; const subMenuHelp: MenuItemConstructorOptions = { @@ -157,27 +126,21 @@ export default class MenuBuilder { { label: 'Learn More', click() { - shell.openExternal('https://electronjs.org'); + shell.openExternal('https://ever.team/'); }, }, { label: 'Documentation', click() { shell.openExternal( - 'https://github.com/electron/electron/tree/main/docs#readme', + 'https://github.com/ever-co/ever-teams/blob/develop/README.md', ); }, }, - { - label: 'Community Discussions', - click() { - shell.openExternal('https://www.electronjs.org/community'); - }, - }, { label: 'Search Issues', click() { - shell.openExternal('https://github.com/electron/electron/issues'); + shell.openExternal('https://github.com/ever-co/ever-teams/issues'); }, }, ], @@ -189,7 +152,7 @@ export default class MenuBuilder { ? subMenuViewDev : subMenuViewProd; - return [subMenuAbout, subMenuEdit, subMenuView, subMenuWindow, subMenuHelp]; + return [subMenuAbout, subMenuView, subMenuWindow, subMenuHelp]; } buildDefaultTemplate() { @@ -197,10 +160,6 @@ export default class MenuBuilder { { label: '&File', submenu: [ - { - label: '&Open', - accelerator: 'Ctrl+O', - }, { label: '&Close', accelerator: 'Ctrl+W', @@ -223,15 +182,6 @@ export default class MenuBuilder { this.mainWindow.webContents.reload(); }, }, - { - label: 'Toggle &Full Screen', - accelerator: 'F11', - click: () => { - this.mainWindow.setFullScreen( - !this.mainWindow.isFullScreen(), - ); - }, - }, { label: 'Toggle &Developer Tools', accelerator: 'Alt+Ctrl+I', @@ -241,15 +191,13 @@ export default class MenuBuilder { }, ] : [ - { - label: 'Toggle &Full Screen', - accelerator: 'F11', - click: () => { - this.mainWindow.setFullScreen( - !this.mainWindow.isFullScreen(), - ); - }, + { + label: 'Toggle &Developer Tools', + accelerator: 'Alt+Ctrl+I', + click: () => { + this.mainWindow.webContents.toggleDevTools(); }, + }, ], }, { @@ -258,27 +206,21 @@ export default class MenuBuilder { { label: 'Learn More', click() { - shell.openExternal('https://electronjs.org'); + shell.openExternal('https://ever.team/'); }, }, { label: 'Documentation', click() { shell.openExternal( - 'https://github.com/electron/electron/tree/main/docs#readme', + 'https://github.com/ever-co/ever-teams/blob/develop/README.md', ); }, }, - { - label: 'Community Discussions', - click() { - shell.openExternal('https://www.electronjs.org/community'); - }, - }, { label: 'Search Issues', click() { - shell.openExternal('https://github.com/electron/electron/issues'); + shell.openExternal('https://github.com/ever-co/ever-teams/issues'); }, }, ], diff --git a/apps/server-web/src/renderer/components/Popup.tsx b/apps/server-web/src/renderer/components/Popup.tsx index f1d51c782..0fdbb9629 100644 --- a/apps/server-web/src/renderer/components/Popup.tsx +++ b/apps/server-web/src/renderer/components/Popup.tsx @@ -46,6 +46,26 @@ export function Popup(props: IPopupComponent) { )} + {props.type === 'warning' && ( +
+
+ + + +
+
+ )} {props.type === 'error' && (
@@ -73,7 +93,7 @@ export function Popup(props: IPopupComponent) { className="text-lg leading-6 font-medium text-gray-900" id="modal-headline" > - {props.type == 'success' + {props.type == 'success' || 'warning' ? t('MESSAGE.SUCCESS') : t('MESSAGE.ERROR')} @@ -84,13 +104,21 @@ export function Popup(props: IPopupComponent) {
-
+
+ {props.closeAction && ( + + )}
diff --git a/apps/server-web/src/renderer/libs/interfaces/i-components.ts b/apps/server-web/src/renderer/libs/interfaces/i-components.ts index dbafae505..770bbb72a 100644 --- a/apps/server-web/src/renderer/libs/interfaces/i-components.ts +++ b/apps/server-web/src/renderer/libs/interfaces/i-components.ts @@ -12,8 +12,9 @@ type IToastComponent = { type IPopupComponent = { isShowPopup: boolean; modalAction: () => void; - type: 'success' | 'error' | 'none'; + type: 'success' | 'error' | 'none' | 'warning'; message: string; + closeAction?: () => void; }; type IProgressComponent = { diff --git a/apps/server-web/src/renderer/libs/interfaces/i-setting.ts b/apps/server-web/src/renderer/libs/interfaces/i-setting.ts index 4b47dd5e5..0053f1eda 100644 --- a/apps/server-web/src/renderer/libs/interfaces/i-setting.ts +++ b/apps/server-web/src/renderer/libs/interfaces/i-setting.ts @@ -32,8 +32,9 @@ interface IServerSetting { } interface IPopup { - type: 'success' | 'error' | 'none'; + type: 'success' | 'error' | 'none' | 'warning'; isShow: boolean; + isDialog: boolean; } interface ILanguages { diff --git a/apps/server-web/src/renderer/pages/Setting.tsx b/apps/server-web/src/renderer/pages/Setting.tsx index 87ef415f1..90b699ac7 100644 --- a/apps/server-web/src/renderer/pages/Setting.tsx +++ b/apps/server-web/src/renderer/pages/Setting.tsx @@ -16,6 +16,7 @@ import { ILanguages, ISideMenu, } from '../libs/interfaces'; +import { useTranslation } from 'react-i18next'; export function Setting() { const [menus, setMenu] = useState([ @@ -35,6 +36,7 @@ export function Setting() { isActive: false, }, ]); + const { t } = useTranslation(); const [updateSetting, setUpdateSetting] = useState({ autoUpdate: false, @@ -64,11 +66,13 @@ export function Setting() { const [popupServer, setPopupServer] = useState({ isShow: false, + isDialog: false, type: 'none', }); const [popupUpdater, setPopupUpdater] = useState({ isShow: false, + isDialog: false, type: 'none', }); @@ -136,6 +140,11 @@ export function Setting() { setPopupServer((prevData) => ({ ...prevData, isShow: !prevData.isShow })); }; + const restartServer = () => { + setPopupServer((prevData) => ({ ...prevData, isShow: !prevData.isShow })); + sendingMessageToMain({}, SettingPageTypeMessage.restartServer); + }; + const setPopupUpdaterState = () => { setPopupUpdater((prevData) => ({ ...prevData, isShow: !prevData.isShow })); }; @@ -147,12 +156,22 @@ export function Setting() { serverSetting={serverSetting} saveSetting={saveSetting} Popup={ - + popupServer.isDialog ? ( + + ) : ( + + ) } /> ); @@ -226,6 +245,7 @@ export function Setting() { setLoading(false); setPopupUpdater({ isShow: true, + isDialog: false, type: 'error', }); break; @@ -252,9 +272,16 @@ export function Setting() { }); break; case SettingPageTypeMessage.mainResponse: + let typeMessage: any; + if (arg.data.status && arg.data.isServerRun) { + typeMessage = 'warning'; + } else { + typeMessage = arg.data.status ? 'success' : 'error'; + } setPopupServer({ isShow: true, - type: arg.data ? 'success' : 'error', + type: typeMessage, + isDialog: arg.data.isServerRun, }); break; case SettingPageTypeMessage.showVersion: