mirror of
https://github.com/tutao/tutanota.git
synced 2025-12-08 06:09:50 +00:00
Instead of applying inbox rules based on the unread mail state in the inbox folder, we introduce the new ProcessingState enum on the mail type. If a mail has been processed by the leader client, which is checking for matching inbox rules, the ProcessingState is updated. If there is a matching rule the flag is updated through the MoveMailService, if there is no matching rule, the flag is updated using the ClientClassifierResultService. Both requests are throttled / debounced. After processing inbox rules, spam prediction is conducted for mails that have not yet been moved by an inbox rule. The ProcessingState for not matching ham mails is also updated using the ClientClassifierResultService. This new inbox rule handing solves the following two problems: - when clicking on a notification it could still happen, that sometimes the inbox rules where not applied - when the inbox folder had a lot of unread mails, the loading time did massively increase, since inbox rules were re-applied on every load Co-authored-by: amm <amm@tutao.de> Co-authored-by: Nick <nif@tutao.de> Co-authored-by: das <das@tutao.de> Co-authored-by: abp <abp@tutao.de> Co-authored-by: jhm <17314077+jomapp@users.noreply.github.com> Co-authored-by: map <mpfau@users.noreply.github.com> Co-authored-by: Kinan <104761667+kibibytium@users.noreply.github.com>
240 lines
7.8 KiB
JavaScript
240 lines
7.8 KiB
JavaScript
/**
|
|
* Copies all currently used libraries from node_modules into libs.
|
|
*
|
|
* We do this to be able to audit changes in the libraries and not rely on npm for checksums.
|
|
*/
|
|
import fs from "fs-extra"
|
|
import path, { dirname } from "node:path"
|
|
import { fileURLToPath } from "node:url"
|
|
import { rollup } from "rollup"
|
|
import { nodeResolve } from "@rollup/plugin-node-resolve"
|
|
import commonjs from "@rollup/plugin-commonjs"
|
|
import child_process from "node:child_process"
|
|
import { promisify } from "node:util"
|
|
import alias from "@rollup/plugin-alias"
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
|
|
async function stripCommentsFromTensorflow() {
|
|
let str = fs.readFileSync("libs/tensorflow.js").toString()
|
|
str = str.replace(/\/\*[\s\S]*?\*\/|(?<=[^:])\/\/.*|^\/\/.*/g, "")
|
|
fs.writeFileSync("libs/tensorflow-stripped.js", str)
|
|
}
|
|
|
|
export async function updateLibs() {
|
|
await copyToLibs(clientDependencies)
|
|
await stripCommentsFromTensorflow()
|
|
}
|
|
|
|
/**
|
|
* Should correspond to {@link import("./RollupConfig").dependencyMap}
|
|
*
|
|
* @typedef {"rollupWeb" | "rollupTF" | "rollupDesktop" | "copy"} BundlingStrategy
|
|
* @typedef {{src: string, target: string, bundling: BundlingStrategy, banner?: string, patch?: string}} DependencyDescription
|
|
* @type Array<DependencyDescription>
|
|
*
|
|
*/
|
|
const clientDependencies = [
|
|
// mithril is patched manually to remove some unused parts
|
|
// "../node_modules/mithril/mithril.js",
|
|
{ src: "../node_modules/mithril/stream/stream.js", target: "stream.js", bundling: "copy" },
|
|
// squire is patched manually to fix issues
|
|
// "../node_modules/squire-rte/dist/squire-raw.mjs",
|
|
{ src: "../node_modules/dompurify/dist/purify.es.mjs", target: "purify.js", bundling: "copy" },
|
|
{ src: "../node_modules/linkifyjs/dist/linkify.mjs", target: "linkify.js", bundling: "copy" },
|
|
{ src: "../node_modules/linkify-html/dist/linkify-html.mjs", target: "linkify-html.js", bundling: "copy" },
|
|
{ src: "../node_modules/luxon/build/es6/luxon.js", target: "luxon.js", bundling: "copy" },
|
|
{ src: "../node_modules/jsqr/dist/jsQR.js", target: "jsQR.js", bundling: "copy" },
|
|
{ src: "../node_modules/jszip/dist/jszip.js", target: "jszip.js", bundling: "rollupWeb" },
|
|
{ src: "../node_modules/cborg/cborg.js", target: "cborg.js", bundling: "rollupWeb" },
|
|
{ src: "../node_modules/qrcode-svg/lib/qrcode.js", target: "qrcode.js", bundling: "rollupWeb" },
|
|
{ src: "../node_modules/electron-updater/out/main.js", target: "electron-updater.mjs", bundling: "rollupDesktop" },
|
|
{ src: "../node_modules/@signalapp/sqlcipher/dist/index.mjs", target: "node-sqlcipher.mjs", bundling: "copy" },
|
|
{ src: "../node_modules/undici/index.js", target: "undici.mjs", bundling: "rollupDesktop" },
|
|
{ src: "../node_modules/@fingerprintjs/botd/dist/botd.esm.js", target: "botd.mjs", bundling: "rollupWeb", patch: "./libs/botd.patch" },
|
|
{ src: "../src/mail-app/workerUtils/spamClassification/tensorflow-custom.js", target: "tensorflow.js", bundling: "rollupTF" },
|
|
]
|
|
|
|
async function applyPatch() {
|
|
// rolldown gets confused when module.exports are used in an expression and wraps everything into a default export
|
|
// remove the problematic parts
|
|
console.log("applying a patch to undici")
|
|
const undiciPath = path.join(__dirname, "../node_modules/undici/index.js")
|
|
const contents = await fs.readFile(undiciPath, { encoding: "utf-8" })
|
|
const replaced = contents
|
|
.replace(
|
|
`const SqliteCacheStore = require('./lib/cache/sqlite-cache-store')
|
|
module.exports.cacheStores.SqliteCacheStore = SqliteCacheStore`,
|
|
"",
|
|
)
|
|
.replace(
|
|
`function install () {
|
|
globalThis.fetch = module.exports.fetch
|
|
globalThis.Headers = module.exports.Headers
|
|
globalThis.Response = module.exports.Response
|
|
globalThis.Request = module.exports.Request
|
|
globalThis.FormData = module.exports.FormData
|
|
globalThis.WebSocket = module.exports.WebSocket
|
|
globalThis.CloseEvent = module.exports.CloseEvent
|
|
globalThis.ErrorEvent = module.exports.ErrorEvent
|
|
globalThis.MessageEvent = module.exports.MessageEvent
|
|
globalThis.EventSource = module.exports.EventSource
|
|
}
|
|
|
|
module.exports.install = install`,
|
|
"",
|
|
)
|
|
await fs.writeFile(undiciPath, replaced, { encoding: "utf-8" })
|
|
}
|
|
|
|
/**
|
|
* applies a git patch file that was created as such:
|
|
* 1. get the unpatched version of whatever library you want to add / change
|
|
* 2. make a commit with the changes that you want to make
|
|
* 3. format the patch by running:
|
|
* git format-patch -k --stdout HEAD~1..HEAD > ./libs/changes.patch
|
|
* 4. revert the commit by running:
|
|
* git reset --hard HEAD~1
|
|
* 5. commit the generated ./libs.changes file
|
|
*/
|
|
async function applyGitPatch(patchFile) {
|
|
if (process.platform === "win32") return
|
|
const exec = promisify(child_process.exec)
|
|
console.log(`applying a patch to ${patchFile}`)
|
|
await exec(`git apply ${patchFile}`)
|
|
}
|
|
|
|
/**
|
|
* @param dependencies {Array<DependencyDescription>}>}
|
|
* @return {Promise<void>}
|
|
*/
|
|
async function copyToLibs(dependencies) {
|
|
await applyPatch()
|
|
|
|
for (let { bundling, src, target, banner, patch } of dependencies) {
|
|
switch (bundling) {
|
|
case "copy":
|
|
await fs.copy(path.join(__dirname, src), path.join(__dirname, "../libs/", target))
|
|
break
|
|
case "rollupWeb":
|
|
await rollWebDep(src, target, banner)
|
|
break
|
|
case "rollupTF":
|
|
await rollupTensorFlow(src, target, banner)
|
|
break
|
|
case "rollupDesktop":
|
|
await rollDesktopDep(src, target, banner)
|
|
break
|
|
default:
|
|
throw new Error(`Unknown bundling strategy: ${bundling}`)
|
|
}
|
|
|
|
if (patch != null) {
|
|
await applyGitPatch(patch)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Will bundle web app dependencies starting at {@param src} into a single file at {@param target}.
|
|
* @type RollupFn
|
|
*/
|
|
async function rollWebDep(src, target, banner) {
|
|
const bundle = await rollup({ input: path.join(__dirname, src), plugins: [nodeResolve()] })
|
|
await bundle.write({ file: path.join(__dirname, "../libs", target), banner })
|
|
}
|
|
|
|
const logResolvePlugin = {
|
|
name: "log-resolve",
|
|
resolveId(source, importer) {
|
|
console.log(`Resolving: source='${source}', importer='${importer}'`)
|
|
return null
|
|
},
|
|
}
|
|
|
|
async function rollupTensorFlow(src, target, banner) {
|
|
const bundle = await rollup({
|
|
input: path.join(__dirname, src),
|
|
treeshake: {
|
|
moduleSideEffects: false,
|
|
preset: "smallest",
|
|
},
|
|
plugins: [
|
|
alias({
|
|
entries: [
|
|
{
|
|
find: /\.\/http/,
|
|
replacement: path.resolve(__dirname, "../libs/tensorflow-http-stub.js"),
|
|
},
|
|
{
|
|
find: /\.\/platforms\/.*/,
|
|
replacement: path.resolve(__dirname, "../libs/tensorflow-platform-stub.js"),
|
|
},
|
|
],
|
|
}),
|
|
// logResolvePlugin,
|
|
nodeResolve(),
|
|
commonjs(),
|
|
],
|
|
output: {
|
|
format: "esm",
|
|
},
|
|
})
|
|
await bundle.write({ file: path.join(__dirname, "../libs", target), banner })
|
|
}
|
|
|
|
/**
|
|
* @typedef {(src: string, target: string, banner: string | undefined) => Promise<void>} RollupFn
|
|
* rollup desktop dependencies with their dependencies into a single esm file
|
|
*
|
|
* specifically, electron-updater is importing some electron internals directly, so we made a comprehensive list of
|
|
* exclusions to not roll up.
|
|
*
|
|
* @type RollupFn
|
|
*/
|
|
async function rollDesktopDep(src, target, banner) {
|
|
const bundle = await rollup({
|
|
input: path.join(__dirname, src),
|
|
makeAbsoluteExternalsRelative: true,
|
|
external: [
|
|
// we handle .node imports ourselves
|
|
/\.node$/,
|
|
"assert",
|
|
"child_process",
|
|
"constants",
|
|
"crypto",
|
|
"electron",
|
|
"events",
|
|
"fs",
|
|
"http",
|
|
"https",
|
|
"os",
|
|
"path",
|
|
"stream",
|
|
"string_decoder",
|
|
"tty",
|
|
"url",
|
|
"util",
|
|
"zlib",
|
|
/.*sqlite-cache-store$/,
|
|
],
|
|
plugins: [
|
|
nodeResolve({ preferBuiltins: true }),
|
|
commonjs({
|
|
ignore: ["node:sqlite"],
|
|
}),
|
|
],
|
|
onwarn: (warning, defaultHandler) => {
|
|
if (warning.code === "CIRCULAR_DEPENDENCY") {
|
|
return // Ignore circular dependency warnings
|
|
}
|
|
defaultHandler(warning)
|
|
},
|
|
})
|
|
await bundle.write({
|
|
file: path.join(__dirname, "../libs", target),
|
|
format: "es",
|
|
// another ugly hack for better-sqlite
|
|
banner,
|
|
})
|
|
}
|