[photos-desktop] Document a bit as I find my way around disabling nodeIntegration (#1170)
* Also includes an unrelated change to update the support email. * See corresponding commit messages for more details about why specific bits of code were removed.
This commit is contained in:
commit
4c33030f28
8 changed files with 106 additions and 96 deletions
|
@ -10,6 +10,12 @@ To know more about Ente, see [our main README](../README.md) or visit
|
|||
|
||||
## Building from source
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> We're improving the security of the desktop app further by migrating to
|
||||
> Electron's sandboxing and contextIsolation. These updates are still WIP and
|
||||
> meanwhile the instructions below might not fully work on the main branch.
|
||||
|
||||
Fetch submodules
|
||||
|
||||
```sh
|
||||
|
@ -28,11 +34,6 @@ Run in development mode (with hot reload)
|
|||
yarn dev
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> `yarn dev` is currently not working (we'll fix soon). If you just want to
|
||||
> build from source and use the generated binary, use `yarn build`.
|
||||
|
||||
Or create a binary for your platform
|
||||
|
||||
```sh
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Electron Updater Example</title>
|
||||
</head>
|
||||
<body>
|
||||
Current version: <span id="version">vX.Y.Z</span>
|
||||
<div id="messages"></div>
|
||||
<script>
|
||||
// Display the current version
|
||||
let version = window.location.hash.substring(1);
|
||||
document.getElementById("version").innerText = version;
|
||||
|
||||
// Listen for messages
|
||||
const { ipcRenderer } = require("electron");
|
||||
ipcRenderer.on("message", function (event, text) {
|
||||
var container = document.getElementById("messages");
|
||||
var message = document.createElement("div");
|
||||
message.innerHTML = text;
|
||||
container.appendChild(message);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -5,36 +5,23 @@ nsis:
|
|||
linux:
|
||||
target:
|
||||
- target: AppImage
|
||||
arch:
|
||||
- x64
|
||||
- arm64
|
||||
arch: [x64, arm64]
|
||||
- target: deb
|
||||
arch:
|
||||
- x64
|
||||
- arm64
|
||||
arch: [x64, arm64]
|
||||
- target: rpm
|
||||
arch:
|
||||
- x64
|
||||
- arm64
|
||||
arch: [x64, arm64]
|
||||
- target: pacman
|
||||
arch:
|
||||
- x64
|
||||
- arm64
|
||||
arch: [x64, arm64]
|
||||
icon: ./resources/icon.icns
|
||||
category: Photography
|
||||
mac:
|
||||
target:
|
||||
target: default
|
||||
arch:
|
||||
- universal
|
||||
arch: [universal]
|
||||
category: public.app-category.photography
|
||||
hardenedRuntime: true
|
||||
x64ArchFiles: Contents/Resources/ggmlclip-mac
|
||||
afterSign: electron-builder-notarize
|
||||
asarUnpack:
|
||||
- node_modules/ffmpeg-static/bin/${os}/${arch}/ffmpeg
|
||||
- node_modules/ffmpeg-static/index.js
|
||||
- node_modules/ffmpeg-static/package.json
|
||||
extraFiles:
|
||||
- from: build
|
||||
to: resources
|
||||
|
|
|
@ -370,7 +370,45 @@ setupLogging();
|
|||
// These objects exposed here will become available to the JS code in our
|
||||
// renderer (the web/ code) as `window.ElectronAPIs.*`
|
||||
//
|
||||
// https://www.electronjs.org/docs/latest/tutorial/tutorial-preload
|
||||
// - Introduction
|
||||
// https://www.electronjs.org/docs/latest/tutorial/tutorial-preload
|
||||
//
|
||||
// There are a few related concepts at play here, and it might be worthwhile to
|
||||
// read their (excellent) documentation to get an understanding;
|
||||
//
|
||||
// - ContextIsolation:
|
||||
// https://www.electronjs.org/docs/latest/tutorial/context-isolation
|
||||
//
|
||||
// - IPC https://www.electronjs.org/docs/latest/tutorial/ipc
|
||||
//
|
||||
//
|
||||
// [Note: Transferring large amount of data over IPC]
|
||||
//
|
||||
// Electron's IPC implementation uses the HTML standard Structured Clone
|
||||
// Algorithm to serialize objects passed between processes.
|
||||
// https://www.electronjs.org/docs/latest/tutorial/ipc#object-serialization
|
||||
//
|
||||
// In particular, both ArrayBuffer and the web File types are eligible for
|
||||
// structured cloning.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
||||
//
|
||||
// Also, ArrayBuffer is "transferable", which means it is a zero-copy operation
|
||||
// operation when it happens across threads.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects
|
||||
//
|
||||
// In our case though, we're not dealing with threads but separate processes,
|
||||
// and it seems like there is a copy involved since the documentation for
|
||||
// contextBridge explicitly calls out that "parameters, errors and return values
|
||||
// are **copied** when they're sent over the bridge".
|
||||
// https://www.electronjs.org/docs/latest/api/context-bridge#methods
|
||||
//
|
||||
// Related is a note from one of Electron's committers stating that even with
|
||||
// copying, the IPC should be fast enough for even moderately large data:
|
||||
// https://github.com/electron/electron/issues/1948#issuecomment-864191345
|
||||
//
|
||||
// The main problem with transfering large amounts of data is potentially
|
||||
// running out of memory, causing the app to crash as it copies it over across
|
||||
// the processes.
|
||||
contextBridge.exposeInMainWorld("ElectronAPIs", {
|
||||
exists,
|
||||
checkExistsAndCreateDir,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import crypto from "crypto";
|
||||
import path from "path";
|
||||
import { existsSync, rename, stat, unlink } from "promise-fs";
|
||||
import { existsSync, stat, unlink } from "promise-fs";
|
||||
import DiskLRUService from "../services/diskLRU";
|
||||
import { LimitedCache } from "../types/cache";
|
||||
import { getFileStream, writeStream } from "./fs";
|
||||
|
@ -44,28 +43,6 @@ export class DiskCache implements LimitedCache {
|
|||
DiskLRUService.touch(cachePath);
|
||||
return new Response(await getFileStream(cachePath));
|
||||
} else {
|
||||
// add fallback for old cache keys
|
||||
const oldCachePath = getOldAssetCachePath(
|
||||
this.cacheBucketDir,
|
||||
cacheKey,
|
||||
);
|
||||
if (existsSync(oldCachePath)) {
|
||||
const fileStats = await stat(oldCachePath);
|
||||
if (sizeInBytes && fileStats.size !== sizeInBytes) {
|
||||
logError(
|
||||
Error(),
|
||||
"Old cache key exists but size does not match. Deleting cache key.",
|
||||
);
|
||||
unlink(oldCachePath).catch((e) => {
|
||||
if (e.code === "ENOENT") return;
|
||||
logError(e, "Failed to delete cache key");
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
const match = new Response(await getFileStream(oldCachePath));
|
||||
void migrateOldCacheKey(oldCachePath, cachePath);
|
||||
return match;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
@ -79,20 +56,3 @@ export class DiskCache implements LimitedCache {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getOldAssetCachePath(cacheDir: string, cacheKey: string) {
|
||||
// hashing the key to prevent illegal filenames
|
||||
const cacheKeyHash = crypto
|
||||
.createHash("sha256")
|
||||
.update(cacheKey)
|
||||
.digest("hex");
|
||||
return path.join(cacheDir, cacheKeyHash);
|
||||
}
|
||||
|
||||
async function migrateOldCacheKey(oldCacheKey: string, newCacheKey: string) {
|
||||
try {
|
||||
await rename(oldCacheKey, newCacheKey);
|
||||
} catch (e) {
|
||||
logError(e, "Failed to move cache key to new cache key");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,33 @@ const INPUT_PATH_PLACEHOLDER = "INPUT";
|
|||
const FFMPEG_PLACEHOLDER = "FFMPEG";
|
||||
const OUTPUT_PATH_PLACEHOLDER = "OUTPUT";
|
||||
|
||||
function getFFmpegStaticPath() {
|
||||
return pathToFfmpeg.replace("app.asar", "app.asar.unpacked");
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a ffmpeg command
|
||||
*
|
||||
* [Note: FFMPEG in Electron]
|
||||
*
|
||||
* There is a wasm build of FFMPEG, but that is currently 10-20 times slower
|
||||
* that the native build. That is slow enough to be unusable for our purposes.
|
||||
* https://ffmpegwasm.netlify.app/docs/performance
|
||||
*
|
||||
* So the alternative is to bundle a ffmpeg binary with our app. e.g.
|
||||
*
|
||||
* yarn add fluent-ffmpeg ffmpeg-static ffprobe-static
|
||||
*
|
||||
* (we only use ffmpeg-static, the rest are mentioned for completeness' sake).
|
||||
*
|
||||
* Interestingly, Electron already bundles an ffmpeg library (it comes from the
|
||||
* ffmpeg fork maintained by Chromium).
|
||||
* https://chromium.googlesource.com/chromium/third_party/ffmpeg
|
||||
* https://stackoverflow.com/questions/53963672/what-version-of-ffmpeg-is-bundled-inside-electron
|
||||
*
|
||||
* This can be found in (e.g. on macOS) at
|
||||
*
|
||||
* $ file ente.app/Contents/Frameworks/Electron\ Framework.framework/Versions/Current/Libraries/libffmpeg.dylib
|
||||
* .../libffmpeg.dylib: Mach-O 64-bit dynamically linked shared library arm64
|
||||
*
|
||||
* I'm not sure if our code is supposed to be able to use it, and how.
|
||||
*/
|
||||
export async function runFFmpegCmd(
|
||||
cmd: string[],
|
||||
inputFilePath: string,
|
||||
|
@ -32,7 +55,7 @@ export async function runFFmpegCmd(
|
|||
|
||||
cmd = cmd.map((cmdPart) => {
|
||||
if (cmdPart === FFMPEG_PLACEHOLDER) {
|
||||
return getFFmpegStaticPath();
|
||||
return ffmpegBinaryPath();
|
||||
} else if (cmdPart === INPUT_PATH_PLACEHOLDER) {
|
||||
return inputFilePath;
|
||||
} else if (cmdPart === OUTPUT_PATH_PLACEHOLDER) {
|
||||
|
@ -76,6 +99,19 @@ export async function runFFmpegCmd(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path to the `ffmpeg` binary.
|
||||
*
|
||||
* At runtime, the ffmpeg binary is present in a path like (macOS example):
|
||||
* `ente.app/Contents/Resources/app.asar.unpacked/node_modules/ffmpeg-static/ffmpeg`
|
||||
*/
|
||||
const ffmpegBinaryPath = () => {
|
||||
// This substitution of app.asar by app.asar.unpacked is suggested by the
|
||||
// ffmpeg-static library author themselves:
|
||||
// https://github.com/eugeneware/ffmpeg-static/issues/16
|
||||
return pathToFfmpeg.replace("app.asar", "app.asar.unpacked");
|
||||
};
|
||||
|
||||
export async function writeTempFile(fileStream: Uint8Array, fileName: string) {
|
||||
const tempFilePath = await generateTempFilePath(fileName);
|
||||
await writeFile(tempFilePath, fileStream);
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
/**
|
||||
* Deprecated - Use File + webUtils.getPathForFile instead
|
||||
*
|
||||
* Electron used to augment the standard web
|
||||
* [File](https://developer.mozilla.org/en-US/docs/Web/API/File) object with an
|
||||
* additional `path` property. This is now deprecated, and will be removed in a
|
||||
* future release.
|
||||
* https://www.electronjs.org/docs/latest/api/file-object
|
||||
*
|
||||
* The alternative to the `path` property is to use `webUtils.getPathForFile`
|
||||
* https://www.electronjs.org/docs/latest/api/web-utils
|
||||
*/
|
||||
export interface ElectronFile {
|
||||
name: string;
|
||||
path: string;
|
||||
|
|
|
@ -46,9 +46,9 @@ export default function HelpSection() {
|
|||
variant="secondary"
|
||||
/>
|
||||
<EnteMenuItem
|
||||
onClick={() => openLink("mailto:contact@ente.io", true)}
|
||||
onClick={() => openLink("mailto:support@ente.io", true)}
|
||||
labelComponent={
|
||||
<NoStyleAnchor href="mailto:contact@ente.io">
|
||||
<NoStyleAnchor href="mailto:support@ente.io">
|
||||
<Typography fontWeight={"bold"}>
|
||||
{t("SUPPORT")}
|
||||
</Typography>
|
||||
|
|
Loading…
Add table
Reference in a new issue