Оптимизация ядра, исправление гонки потоков при получении версии

This commit is contained in:
RoyceDa
2026-02-20 16:34:54 +02:00
parent f01ed34285
commit 2e18d489be
17 changed files with 196 additions and 101 deletions

View File

@@ -10,8 +10,6 @@ on:
paths:
- 'lib/**'
jobs:
build:
runs-on: windows-latest

View File

@@ -7,14 +7,16 @@ import { useHotkeys } from "@mantine/hooks";
import { useNavigate } from "react-router-dom";
import { usePublicKey } from "@/app/providers/AccountProvider/usePublicKey";
import { DialogHeaderText } from "../DialogHeaderText/DialogHeaderText";
import { useCoreDevice } from "@/app/providers/DeviceProvider/useCoreDevice";
export function DialogsPanelHeader() {
const colors = useRosettaColors();
const logout = useLogout();
const navigate = useNavigate();
const publicKey = usePublicKey();
const viewKeys = window.platform == 'darwin' ? '⌘' : 'Ctrl+';
const triggerKeys = window.platform == 'darwin' ? 'mod' : 'Ctrl';
const {platform} = useCoreDevice();
const viewKeys = platform == 'darwin' ? '' : 'Ctrl+';
const triggerKeys = platform == 'darwin' ? 'mod' : 'Ctrl';
useHotkeys([
[`${triggerKeys}+L`, () => logout()],

View File

@@ -6,6 +6,7 @@ import { IconArrowDown, IconFile, IconX } from "@tabler/icons-react";
import { dotCenterIfNeeded, humanFilesize } from "@/app/utils/utils";
import { AnimatedRoundedProgress } from "../AnimatedRoundedProgress/AnimatedRoundedProgress";
import { DeliveredMessageState } from "@/app/providers/DialogProvider/DialogProvider";
import { useCore } from "@/app/hooks/useCore";
export function MessageFile(props : AttachmentProps) {
const colors = useRosettaColors();
@@ -27,15 +28,15 @@ export function MessageFile(props : AttachmentProps) {
const filetype = filename.split(".")[filename.split(".").length - 1];
const isEncrypting = props.delivered == DeliveredMessageState.WAITING && uploadedPercentage <= 0;
const isUploading = props.delivered == DeliveredMessageState.WAITING && uploadedPercentage > 0 && uploadedPercentage < 100;
const {getDownloadsPath} = useCore();
const onClick = async () => {
if(downloadStatus == DownloadStatus.ERROR){
return;
}
if(downloadStatus == DownloadStatus.DOWNLOADED){
//let content = await getBlob();
//let buffer = Buffer.from(content.split(",")[1], 'base64');
let pathInDownloads = window.downloadsPath + "/Rosetta Downloads/" + filename;
const downloadsPath = await getDownloadsPath();
let pathInDownloads = downloadsPath + "/Rosetta Downloads/" + filename;
//await writeFile(pathInDownloads, buffer, false);
window.shell.showItemInFolder(pathInDownloads);
return;

View File

@@ -5,17 +5,19 @@ import { useProtocolState } from "@/app/providers/ProtocolProvider/useProtocolSt
import { ProtocolState } from "@/app/providers/ProtocolProvider/ProtocolProvider";
import { WindowsFrameButtons } from "../WindowsFrameButtons/WindowsFrameButtons";
import { MacFrameButtons } from "../MacFrameButtons/MacFrameButtons";
import { useCoreDevice } from "@/app/providers/DeviceProvider/useCoreDevice";
export function Topbar() {
const colors = useRosettaColors();
const [protocolState] = useProtocolState();
const {platform} = useCoreDevice();
return (
<Box className={classes.drag} ta={'center'} p={3} bg={colors.mainColor}>
{window.platform == 'win32' && <WindowsFrameButtons></WindowsFrameButtons>}
{window.platform == 'darwin' && <MacFrameButtons></MacFrameButtons>}
{window.platform == 'linux' && <WindowsFrameButtons></WindowsFrameButtons>}
{platform == 'win32' && <WindowsFrameButtons></WindowsFrameButtons>}
{platform == 'darwin' && <MacFrameButtons></MacFrameButtons>}
{platform == 'linux' && <WindowsFrameButtons></WindowsFrameButtons>}
{(protocolState == ProtocolState.CONNECTED || protocolState == ProtocolState.SYNCHRONIZATION || !window.location.hash.includes("main")) &&
<Flex align={'center'} justify={'center'}>
<Text fw={'bolder'} fz={13} c={'gray'}>

View File

@@ -1,13 +1,7 @@
import { AttachmentType } from "./providers/ProtocolProvider/protocol/packets/packet.message";
export const CORE_VERSION = window.version || "1.0.0";
/**
* Application directives
*/
export const APPLICATION_PLATFROM = window.platform || "unknown";
export const APPLICATION_ARCH = window.arch || "unknown";
export const APP_PATH = window.appPath || ".";
export const SIZE_LOGIN_WIDTH_PX = 300;
export const DEVTOOLS_CHEATCODE = "rosettadev1";
export const AVATAR_PASSWORD_TO_ENCODE = "rosetta-a";
@@ -62,5 +56,6 @@ export const ALLOWED_DOMAINS_ZONES = [
'gg',
'fm',
'tv',
'im'
'im',
'sc'
];

62
app/hooks/useCore.ts Normal file
View File

@@ -0,0 +1,62 @@
export function useCore() {
const openExternal = (url: string) => {
window.shell.openExternal(url);
};
const showItemInFolder = (fullPath: string) => {
window.shell.showItemInFolder(fullPath);
};
const getCoreVersion = async () => {
const version = await window.electron.ipcRenderer.invoke('ipcCore:getCoreVersion');
return version;
}
const getArch = async () => {
const arch = await window.electron.ipcRenderer.invoke('ipcCore:getArch');
return arch;
}
const getUserDir = async () => {
const userDir = await window.electron.ipcRenderer.invoke('ipcCore:getUserDir');
return userDir;
}
const getAppPath = async () => {
const appPath = await window.electron.ipcRenderer.invoke('ipcCore:getAppPath');
return appPath;
}
const getDownloadsPath = async () => {
const downloadsPath = await window.electron.ipcRenderer.invoke('ipcCore:getDownloadsPath');
return downloadsPath;
}
const getPlatform = async () => {
const platform = await window.electron.ipcRenderer.invoke('ipcCore:getPlatform');
return platform;
}
const getDeviceName = async () => {
const deviceName = await window.electron.ipcRenderer.invoke('device:name');
return deviceName;
}
const getDeviceId = async () => {
const deviceId = await window.electron.ipcRenderer.invoke('device:id');
return deviceId;
}
return {
openExternal,
showItemInFolder,
getCoreVersion,
getArch,
getUserDir,
getAppPath,
getDownloadsPath,
getPlatform,
getDeviceName,
getDeviceId
}
}

View File

View File

@@ -14,6 +14,7 @@ import { DialogContext } from "../DialogProvider/DialogProvider";
import { useSaveAvatar } from "../AvatarProvider/useSaveAvatar";
import { AVATAR_PASSWORD_TO_ENCODE } from "@/app/constants";
import { useDialog } from "../DialogProvider/useDialog";
import { useCore } from "@/app/hooks/useCore";
export enum DownloadStatus {
DOWNLOADED,
@@ -37,6 +38,7 @@ export function useAttachment(attachment: Attachment, keyPlain: string) {
const {updateAttachmentInDialogCache} = useDialogsCache();
const {info} = useConsoleLogger('useAttachment');
const {updateAttachmentsInMessagesByAttachmentId} = useDialog();
const {getDownloadsPath} = useCore();
const context = useContext(DialogContext);
@@ -85,7 +87,8 @@ export function useAttachment(attachment: Attachment, keyPlain: string) {
const preview = getPreview();
const filesize = parseInt(preview.split("::")[0]);
const filename = preview.split("::")[1];
let pathInDownloads = window.downloadsPath + "/Rosetta Downloads/" + filename;
const downloadsPath = await getDownloadsPath();
let pathInDownloads = downloadsPath + "/Rosetta Downloads/" + filename;
const exists = await fileExists(pathInDownloads, false);
const existsLength = await size(pathInDownloads, false);
if(exists && existsLength == filesize){
@@ -161,8 +164,9 @@ export function useAttachment(attachment: Attachment, keyPlain: string) {
*/
const preview = getPreview();
const filename = preview.split("::")[1];
const downloadsPath = await getDownloadsPath();
let buffer = Buffer.from(decrypted.split(",")[1], 'base64');
let pathInDownloads = window.downloadsPath + "/Rosetta Downloads/" + filename;
let pathInDownloads = downloadsPath + "/Rosetta Downloads/" + filename;
/**
* Пишем файл в загрузки, но перед этим выбираем ему название, если файл в загрузках
* уже есть с таким названием то добавляем к названию (1), (2) и так далее, чтобы не перезаписать существующий файл
@@ -170,7 +174,7 @@ export function useAttachment(attachment: Attachment, keyPlain: string) {
let finalPath = pathInDownloads;
let fileIndex = 1;
while (await fileExists(finalPath, false)) {
finalPath = window.downloadsPath + "/Rosetta Downloads/" + filename.split(".").slice(0, -1).join(".") + ` (${fileIndex})` + "." + filename.split(".").slice(-1);
finalPath = downloadsPath + "/Rosetta Downloads/" + filename.split(".").slice(0, -1).join(".") + ` (${fileIndex})` + "." + filename.split(".").slice(-1);
fileIndex++;
}
await writeFile(finalPath, buffer, false);

View File

@@ -0,0 +1,33 @@
import { useCore } from "@/app/hooks/useCore";
import { useEffect, useState } from "react";
export function useCoreDevice() : {
deviceId: string;
deviceName: string;
platform: string;
} {
const { getDeviceId, getDeviceName, getPlatform } = useCore();
const [deviceId, setDeviceId] = useState<string>("");
const [deviceName, setDeviceName] = useState<string>("");
const [platform, setPlatform] = useState<string>("");
useEffect(() => {
fetchDeviceInfo();
}, []);
const fetchDeviceInfo = async () => {
const deviceId = await getDeviceId();
const deviceName = await getDeviceName();
const platform = await getPlatform();
setDeviceId(deviceId);
setDeviceName(deviceName);
setPlatform(platform);
console.info("Device info - ID:", deviceId, "Name:", deviceName);
}
return {
deviceId,
deviceName,
platform
}
}

View File

@@ -2,6 +2,7 @@ import { decodeWithPassword, encodeWithPassword } from "@/app/workers/crypto/cry
import { useFileStorage } from "@/app/hooks/useFileStorage";
import { generateRandomKey } from "@/app/utils/utils";
import { createContext, useEffect, useState } from "react";
import { useCore } from "@/app/hooks/useCore";
interface SystemProviderContextValue {
id: string;
@@ -21,7 +22,10 @@ export interface SystemProviderProps {
*/
export function SystemProvider(props: SystemProviderProps) {
const [deviceId, setDeviceId] = useState<string>("");
const [deviceName, setDeviceName] = useState<string>("");
const [deviceOs, setDeviceOs] = useState<string>("");
const {writeFile, readFile} = useFileStorage();
const { getDeviceId, getDeviceName, getPlatform } = useCore();
useEffect(() => {
fetchDeviceId();
@@ -29,6 +33,10 @@ export function SystemProvider(props: SystemProviderProps) {
const fetchDeviceId = async () => {
const device = await readFile("device");
const name = await getDeviceName();
const platform = await getPlatform();
setDeviceName(name);
setDeviceOs(platform);
if(device){
const decoded = await decodeDevice(Buffer.from(device).toString('utf-8'));
if(decoded){
@@ -47,12 +55,11 @@ export function SystemProvider(props: SystemProviderProps) {
}
const decodeDevice = async (data: string) => {
const hwid = window.deviceId;
const platform = window.deviceName;
const hwid = await getDeviceId();
const deviceName = await getDeviceName();
const salt = "rosetta-device-salt";
try {
const decoded = await decodeWithPassword(hwid + platform + salt, data);
const decoded = await decodeWithPassword(hwid + deviceName + salt, data);
return decoded;
} catch (e) {
console.error("Failed to decode device data:", e);
@@ -61,29 +68,24 @@ export function SystemProvider(props: SystemProviderProps) {
}
const encodeDevice = async (data: string) => {
const hwid = window.deviceId;
const platform = window.deviceName;
const hwid = await getDeviceId();
const deviceName = await getDeviceName();
const salt = "rosetta-device-salt";
try {
const encoded = await encodeWithPassword(hwid + platform + salt, data);
const encoded = await encodeWithPassword(hwid + deviceName + salt, data);
return encoded;
} catch (e) {
console.error("Failed to encode device data:", e);
return null;
}
}
const systemName = window.deviceName || "Unknown Device";
const systemOs = window.platform || "Unknown OS";
return (
<SystemProviderContext.Provider value={{
id: deviceId,
name: systemName,
os: systemOs
name: deviceName || "Unknown Device",
os: deviceOs || "Unknown OS"
}}>
{props.children}
</SystemProviderContext.Provider>

View File

@@ -4,8 +4,8 @@ import { useSender } from "../ProtocolProvider/useSender";
import { usePacket } from "../ProtocolProvider/usePacket";
import { useConsoleLogger } from "@/app/hooks/useConsoleLogger";
import { useFileStorage } from "@/app/hooks/useFileStorage";
import { APPLICATION_ARCH, APPLICATION_PLATFROM, CORE_VERSION } from "@/app/constants";
import { APP_VERSION } from "@/app/version";
import { useCore } from "@/app/hooks/useCore";
export interface UpdateProviderProps {
children: React.ReactNode;
@@ -58,6 +58,7 @@ export function UpdateProvider(props: UpdateProviderProps) {
const [appUpdateUrl, setAppUpdateUrl] = useState<string>("");
const [appActualVersion, setAppActualVersion] = useState<string>("");
const {writeFile} = useFileStorage();
const {getCoreVersion, getArch, getPlatform} = useCore();
useEffect(() => {
let packet = new PacketRequestUpdate();
@@ -75,6 +76,9 @@ export function UpdateProvider(props: UpdateProviderProps) {
}, []);
const checkForUpdates = async () => {
const coreVersion = await getCoreVersion();
const arch = await getArch();
const platform = await getPlatform();
if(updateServerRef.current == null){
/**
* SDU еще не определен
@@ -85,7 +89,7 @@ export function UpdateProvider(props: UpdateProviderProps) {
* Запрашиваем обновления с SDU сервера
*/
let response = await fetch
(`${updateServerRef.current}/updates/get?app=${APP_VERSION}&kernel=${CORE_VERSION}&arch=${APPLICATION_ARCH}&platform=${APPLICATION_PLATFROM}`).catch((e) => {
(`${updateServerRef.current}/updates/get?app=${APP_VERSION}&kernel=${coreVersion}&arch=${arch}&platform=${platform}`).catch((e) => {
error("Failed to check for updates: " + e.message);
});
if(!response || response.status != 200){

View File

@@ -10,11 +10,14 @@ import { AnimatedButton } from "@/app/components/AnimatedButton/AnimatedButton";
import { useLogout } from "@/app/providers/AccountProvider/useLogout";
import { usePacket } from "@/app/providers/ProtocolProvider/usePacket";
import { PacketDeviceResolve, Solution } from "@/app/providers/ProtocolProvider/protocol/packets/packet.device.resolve";
import { useCoreDevice } from "@/app/providers/DeviceProvider/useCoreDevice";
export function DeviceConfirm() {
const [protocolState] = useProtocolState();
const navigate = useNavigate();
const logout = useLogout();
const {deviceName} = useCoreDevice();
useEffect(() => {
if(protocolState == ProtocolState.CONNECTED) {
@@ -60,7 +63,7 @@ export function DeviceConfirm() {
<Flex justify={'center'} mt={'xl'} px={'lg'} align={'center'}>
<Flex justify={'center'} gap={'sm'} align={'center'}>
<Text ta={'center'} c={'dimmed'} fz={12}>
Confirm device <strong>{window.deviceName}</strong> on your first device to loading your chats.
Confirm device <strong>{deviceName}</strong> on your first device to loading your chats.
</Text>
</Flex>
</Flex>

View File

@@ -4,15 +4,26 @@ import { RosettaPower } from "@/app/components/RosettaPower/RosettaPower";
import { SettingsAlert } from "@/app/components/SettingsAlert/SettingsAlert";
import { SettingsInput } from "@/app/components/SettingsInput/SettingsInput";
import { UpdateAlert } from "@/app/components/UpdateAlert/UpdateAlert";
import { CORE_VERSION } from "@/app/constants";
import { useCore } from "@/app/hooks/useCore";
import { UpdateStatus } from "@/app/providers/UpdateProvider/UpdateProvider";
import { useUpdater } from "@/app/providers/UpdateProvider/useUpdater";
import { APP_VERSION } from "@/app/version";
import { Box, Text } from "@mantine/core";
import { useEffect, useState } from "react";
export function Update() {
const {updateStatus} = useUpdater();
const {getCoreVersion} = useCore();
const [coreVersion, setCoreVersion] = useState<string>("");
useEffect(() => {
const fetchCoreVersion = async () => {
const version = await getCoreVersion();
setCoreVersion(version);
}
fetchCoreVersion();
}, [getCoreVersion]);
return (
<>
<Breadcrumbs text="Updates"></Breadcrumbs>
@@ -23,7 +34,7 @@ export function Update() {
<Box mt={'sm'}>
<UpdateAlert radius={'sm'}></UpdateAlert>
</Box>
<SettingsInput.Copy mt={'sm'} hit="Kernel" value={CORE_VERSION}></SettingsInput.Copy>
<SettingsInput.Copy mt={'sm'} hit="Kernel" value={coreVersion}></SettingsInput.Copy>
<Text fz={10} mt={3} c={'gray'} pl={'xs'} pr={'xs'}>
If the kernel version is outdated, you need to reinstall the application so that this kernel continues to receive current updates.
</Text>

View File

@@ -1,4 +1,4 @@
import { BrowserWindow, shell, app, ipcMain, nativeTheme, screen, powerMonitor } from 'electron'
import { BrowserWindow, shell, ipcMain, nativeTheme, screen, powerMonitor } from 'electron'
import { join } from 'path'
import fs from 'fs'
import { WORKING_DIR } from './constants';
@@ -79,11 +79,6 @@ export function foundationIpcRegistration(mainWindow: BrowserWindow) {
ipcMain.removeAllListeners("write-file");
ipcMain.removeAllListeners("read-file");
ipcMain.removeAllListeners("mkdir");
ipcMain.removeHandler("get-core-version");
ipcMain.removeHandler("get-arch");
ipcMain.removeAllListeners("get-user-dir");
ipcMain.removeHandler("get-downloads-path")
ipcMain.removeHandler("get-app-path");
ipcMain.removeHandler('open-dev-tools');
ipcMain.removeHandler('window-state');
ipcMain.removeHandler('window-toggle');
@@ -92,14 +87,6 @@ export function foundationIpcRegistration(mainWindow: BrowserWindow) {
ipcMain.removeHandler('showItemInFolder');
ipcMain.removeHandler('openExternal');
ipcMain.handle('showItemInFolder', (_, fullPath: string) => {
shell.showItemInFolder(fullPath);
});
ipcMain.handle('openExternal', (_, url: string) => {
shell.openExternal(url);
});
ipcMain.handle('open-dev-tools', () => {
if (mainWindow.webContents.isDevToolsOpened()) {
return;
@@ -208,27 +195,4 @@ export function foundationIpcRegistration(mainWindow: BrowserWindow) {
mainWindow.webContents.send("mkdir-reply");
});
});
/**
* Change to get-core-version
*/
ipcMain.handle("get-core-version", () => {
return app.getVersion();
});
ipcMain.handle("get-arch", () => {
return process.arch;
})
ipcMain.on("get-user-dir", () => {
const userDir = app.getPath("userData");
mainWindow.webContents.send("get-user-dir-reply", userDir);
});
ipcMain.handle("get-app-path", () => {
return app.getAppPath();
});
ipcMain.handle("get-downloads-path", () => {
return app.getPath("downloads");
});
}

34
lib/main/ipcs/ipcCore.ts Normal file
View File

@@ -0,0 +1,34 @@
import { app, ipcMain, shell } from "electron";
ipcMain.handle("ipcCore:getCoreVersion", () => {
return app.getVersion();
});
ipcMain.handle("ipcCore:getArch", () => {
return process.arch;
})
ipcMain.handle("ipcCore:getUserDir", () => {
const userDir = app.getPath("userData");
return userDir;
});
ipcMain.handle("ipcCore:getAppPath", () => {
return app.getAppPath();
});
ipcMain.handle("ipcCore:getDownloadsPath", () => {
return app.getPath("downloads");
});
ipcMain.handle('ipcCore:showItemInFolder', (_, fullPath: string) => {
shell.showItemInFolder(fullPath);
});
ipcMain.handle('ipcCore:openExternal', (_, url: string) => {
shell.openExternal(url);
});
ipcMain.handle('ipcCore:getPlatform', () => {
return process.platform;
});

View File

@@ -7,6 +7,7 @@ import './ipcs/ipcFilestorage'
import './ipcs/ipcUpdate'
import './ipcs/ipcNotification'
import './ipcs/ipcDevice'
import './ipcs/ipcCore'
import { Tray } from 'electron/main'
import { join } from 'path'
import { Logger } from './logger'

View File

@@ -4,46 +4,25 @@ import api from './api'
const exposeContext = async () => {
let version = await ipcRenderer.invoke("get-core-version");
let appPath = await ipcRenderer.invoke("get-app-path");
let arch = await ipcRenderer.invoke("get-arch");
let deviceName = await ipcRenderer.invoke("device:name");
let deviceId = await ipcRenderer.invoke("device:id");
let downloadsPath = await ipcRenderer.invoke("get-downloads-path");
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
contextBridge.exposeInMainWorld('version', version);
contextBridge.exposeInMainWorld('platform', process.platform);
contextBridge.exposeInMainWorld('appPath', appPath);
contextBridge.exposeInMainWorld('arch', arch);
contextBridge.exposeInMainWorld('deviceName', deviceName);
contextBridge.exposeInMainWorld('deviceId', deviceId);
contextBridge.exposeInMainWorld('shell', {
openExternal: (url: string) => {
ipcRenderer.invoke('openExternal', url);
ipcRenderer.invoke('ipcCore:openExternal', url);
},
showItemInFolder: (fullPath: string) => {
ipcRenderer.invoke('showItemInFolder', fullPath);
ipcRenderer.invoke('ipcCore:showItemInFolder', fullPath);
}
});
contextBridge.exposeInMainWorld('downloadsPath', downloadsPath)
} catch (error) {
console.error(error)
}
} else {
window.electron = electronAPI
window.api = api
window.version = version;
window.platform = process.platform;
window.appPath = appPath;
window.arch = arch;
window.api = api;
window.shell = shell;
window.downloadsPath = downloadsPath;
window.deviceName = deviceName;
window.deviceId = deviceId;
}
}