(feat): improve local storage security (feat): move settings to dedicated settings package
89 lines
3 KiB
TypeScript
89 lines
3 KiB
TypeScript
import { contextBridge, ipcRenderer } from "electron";
|
|
import {
|
|
ipcChannels,
|
|
type DesktopCallStatus,
|
|
type DesktopScreenShareSource,
|
|
secureStorageLimits,
|
|
} from "../shared/ipc.js";
|
|
|
|
function windowAction(channel: string) {
|
|
return () => ipcRenderer.invoke(channel);
|
|
}
|
|
|
|
function validKey(key: string) {
|
|
return (
|
|
typeof key === "string" &&
|
|
key.length > 0 &&
|
|
Buffer.byteLength(key, "utf8") <= secureStorageLimits.maxKeyBytes
|
|
);
|
|
}
|
|
|
|
const desktopApi = {
|
|
media: {
|
|
listScreenShareSources: () =>
|
|
ipcRenderer.invoke(ipcChannels.listScreenShareSources),
|
|
listScreenShareAudioOutputs: () =>
|
|
ipcRenderer.invoke(ipcChannels.listScreenShareAudioOutputs),
|
|
getScreenShareCapabilities: () =>
|
|
ipcRenderer.invoke(ipcChannels.getScreenShareCapabilities),
|
|
selectScreenShareSource: (sourceId: DesktopScreenShareSource["id"]) => {
|
|
if (typeof sourceId !== "string" || sourceId.length === 0) {
|
|
return Promise.reject(new Error("Invalid screen share source id."));
|
|
}
|
|
|
|
return ipcRenderer.invoke(ipcChannels.selectScreenShareSource, sourceId);
|
|
},
|
|
},
|
|
app: {
|
|
getVersion: () => ipcRenderer.invoke(ipcChannels.getVersion),
|
|
},
|
|
updates: {
|
|
checkForUpdates: () => ipcRenderer.invoke(ipcChannels.checkForUpdates),
|
|
},
|
|
call: {
|
|
setStatus: (status: DesktopCallStatus) => {
|
|
if (
|
|
typeof status?.inCall !== "boolean" ||
|
|
typeof status?.speaking !== "boolean" ||
|
|
(status.iconDataUrl !== undefined &&
|
|
(typeof status.iconDataUrl !== "string" ||
|
|
!status.iconDataUrl.startsWith("data:image/png;base64,")))
|
|
) {
|
|
return Promise.reject(new Error("Invalid call status."));
|
|
}
|
|
|
|
return ipcRenderer.invoke(ipcChannels.setCallStatus, status);
|
|
},
|
|
},
|
|
secureStorage: {
|
|
getStatus: () => ipcRenderer.invoke(ipcChannels.getSecureStorageStatus),
|
|
load: (key: string) =>
|
|
validKey(key)
|
|
? ipcRenderer.invoke(ipcChannels.loadSecureStorage, key)
|
|
: Promise.reject(new Error("Invalid secure storage key.")),
|
|
save: (key: string, value: string) =>
|
|
validKey(key) &&
|
|
typeof value === "string" &&
|
|
Buffer.byteLength(value, "utf8") <= secureStorageLimits.maxValueBytes
|
|
? ipcRenderer.invoke(ipcChannels.saveSecureStorage, key, value)
|
|
: Promise.reject(new Error("Invalid secure storage key or value.")),
|
|
delete: (key: string) =>
|
|
validKey(key)
|
|
? ipcRenderer.invoke(ipcChannels.deleteSecureStorage, key)
|
|
: Promise.reject(new Error("Invalid secure storage key.")),
|
|
clear: () => ipcRenderer.invoke(ipcChannels.clearSecureStorage),
|
|
},
|
|
window: {
|
|
minimize: () => windowAction(ipcChannels.minimizeWindow),
|
|
maximize: () => windowAction(ipcChannels.maximizeWindow),
|
|
close: () => windowAction(ipcChannels.closeWindow),
|
|
},
|
|
};
|
|
|
|
contextBridge.exposeInMainWorld("tensaminDesktop", desktopApi);
|
|
contextBridge.exposeInMainWorld(
|
|
"tensaminShowWindowControls",
|
|
process.env.TENSAMIN_HIDE_CONTROLS == null,
|
|
);
|
|
|
|
export type TensaminDesktopApi = typeof desktopApi;
|