fix: Cleanup PR
This commit is contained in:
1
.github/workflows/electron.yml
vendored
1
.github/workflows/electron.yml
vendored
@@ -746,7 +746,6 @@ jobs:
|
|||||||
mkdir -p homebrew-submission/Casks/t
|
mkdir -p homebrew-submission/Casks/t
|
||||||
|
|
||||||
cp homebrew/termix.rb homebrew-submission/Casks/t/termix.rb
|
cp homebrew/termix.rb homebrew-submission/Casks/t/termix.rb
|
||||||
cp homebrew/README.md homebrew-submission/
|
|
||||||
|
|
||||||
sed -i '' "s/VERSION_PLACEHOLDER/$VERSION/g" homebrew-submission/Casks/t/termix.rb
|
sed -i '' "s/VERSION_PLACEHOLDER/$VERSION/g" homebrew-submission/Casks/t/termix.rb
|
||||||
sed -i '' "s/CHECKSUM_PLACEHOLDER/$CHECKSUM/g" homebrew-submission/Casks/t/termix.rb
|
sed -i '' "s/CHECKSUM_PLACEHOLDER/$CHECKSUM/g" homebrew-submission/Casks/t/termix.rb
|
||||||
|
|||||||
@@ -59,29 +59,9 @@ class DatabaseFileEncryption {
|
|||||||
dataSize: encrypted.length,
|
dataSize: encrypted.length,
|
||||||
};
|
};
|
||||||
|
|
||||||
databaseLogger.debug("Starting atomic encryption write", {
|
|
||||||
operation: "database_buffer_encryption_start",
|
|
||||||
targetPath,
|
|
||||||
tmpPath,
|
|
||||||
originalSize: buffer.length,
|
|
||||||
encryptedSize: encrypted.length,
|
|
||||||
keyFingerprint,
|
|
||||||
ivPrefix: metadata.iv.substring(0, 8),
|
|
||||||
tagPrefix: metadata.tag.substring(0, 8),
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.writeFileSync(tmpPath, encrypted);
|
fs.writeFileSync(tmpPath, encrypted);
|
||||||
fs.writeFileSync(tmpMetadataPath, JSON.stringify(metadata, null, 2));
|
fs.writeFileSync(tmpMetadataPath, JSON.stringify(metadata, null, 2));
|
||||||
|
|
||||||
databaseLogger.debug(
|
|
||||||
"Temporary files written, performing atomic rename",
|
|
||||||
{
|
|
||||||
operation: "database_buffer_encryption_rename",
|
|
||||||
tmpPath,
|
|
||||||
targetPath,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (fs.existsSync(targetPath)) {
|
if (fs.existsSync(targetPath)) {
|
||||||
fs.unlinkSync(targetPath);
|
fs.unlinkSync(targetPath);
|
||||||
}
|
}
|
||||||
@@ -92,13 +72,6 @@ class DatabaseFileEncryption {
|
|||||||
}
|
}
|
||||||
fs.renameSync(tmpMetadataPath, metadataPath);
|
fs.renameSync(tmpMetadataPath, metadataPath);
|
||||||
|
|
||||||
databaseLogger.debug("Database buffer encrypted with atomic write", {
|
|
||||||
operation: "database_buffer_encryption_atomic",
|
|
||||||
targetPath,
|
|
||||||
encryptedSize: encrypted.length,
|
|
||||||
keyFingerprint,
|
|
||||||
});
|
|
||||||
|
|
||||||
return targetPath;
|
return targetPath;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
try {
|
try {
|
||||||
@@ -177,29 +150,9 @@ class DatabaseFileEncryption {
|
|||||||
dataSize: encrypted.length,
|
dataSize: encrypted.length,
|
||||||
};
|
};
|
||||||
|
|
||||||
databaseLogger.debug("Starting atomic file encryption", {
|
|
||||||
operation: "database_file_encryption_start",
|
|
||||||
sourcePath,
|
|
||||||
encryptedPath,
|
|
||||||
tmpPath,
|
|
||||||
originalSize: sourceData.length,
|
|
||||||
encryptedSize: encrypted.length,
|
|
||||||
keyFingerprint,
|
|
||||||
ivPrefix: metadata.iv.substring(0, 8),
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.writeFileSync(tmpPath, encrypted);
|
fs.writeFileSync(tmpPath, encrypted);
|
||||||
fs.writeFileSync(tmpMetadataPath, JSON.stringify(metadata, null, 2));
|
fs.writeFileSync(tmpMetadataPath, JSON.stringify(metadata, null, 2));
|
||||||
|
|
||||||
databaseLogger.debug(
|
|
||||||
"Temporary files written, performing atomic rename",
|
|
||||||
{
|
|
||||||
operation: "database_file_encryption_rename",
|
|
||||||
tmpPath,
|
|
||||||
encryptedPath,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (fs.existsSync(encryptedPath)) {
|
if (fs.existsSync(encryptedPath)) {
|
||||||
fs.unlinkSync(encryptedPath);
|
fs.unlinkSync(encryptedPath);
|
||||||
}
|
}
|
||||||
@@ -267,31 +220,9 @@ class DatabaseFileEncryption {
|
|||||||
const dataFileStats = fs.statSync(encryptedPath);
|
const dataFileStats = fs.statSync(encryptedPath);
|
||||||
const metaFileStats = fs.statSync(metadataPath);
|
const metaFileStats = fs.statSync(metadataPath);
|
||||||
|
|
||||||
databaseLogger.debug("Starting database decryption", {
|
|
||||||
operation: "database_buffer_decryption_start",
|
|
||||||
encryptedPath,
|
|
||||||
metadataPath,
|
|
||||||
dataFileSize: dataFileStats.size,
|
|
||||||
dataFileMtime: dataFileStats.mtime.toISOString(),
|
|
||||||
metaFileMtime: metaFileStats.mtime.toISOString(),
|
|
||||||
dataDir: process.env.DATA_DIR || "./db/data",
|
|
||||||
});
|
|
||||||
|
|
||||||
const metadataContent = fs.readFileSync(metadataPath, "utf8");
|
const metadataContent = fs.readFileSync(metadataPath, "utf8");
|
||||||
const metadata: EncryptedFileMetadata = JSON.parse(metadataContent);
|
const metadata: EncryptedFileMetadata = JSON.parse(metadataContent);
|
||||||
|
|
||||||
databaseLogger.debug("Metadata loaded", {
|
|
||||||
operation: "database_metadata_loaded",
|
|
||||||
version: metadata.version,
|
|
||||||
algorithm: metadata.algorithm,
|
|
||||||
keySource: metadata.keySource,
|
|
||||||
fingerprint: metadata.fingerprint,
|
|
||||||
hasDataSize: !!metadata.dataSize,
|
|
||||||
expectedDataSize: metadata.dataSize,
|
|
||||||
ivPrefix: metadata.iv?.substring(0, 8),
|
|
||||||
tagPrefix: metadata.tag?.substring(0, 8),
|
|
||||||
});
|
|
||||||
|
|
||||||
const encryptedData = fs.readFileSync(encryptedPath);
|
const encryptedData = fs.readFileSync(encryptedPath);
|
||||||
|
|
||||||
if (metadata.dataSize && encryptedData.length !== metadata.dataSize) {
|
if (metadata.dataSize && encryptedData.length !== metadata.dataSize) {
|
||||||
@@ -342,15 +273,6 @@ class DatabaseFileEncryption {
|
|||||||
.digest("hex")
|
.digest("hex")
|
||||||
.substring(0, 16);
|
.substring(0, 16);
|
||||||
|
|
||||||
databaseLogger.debug("Starting decryption with loaded key", {
|
|
||||||
operation: "database_decryption_attempt",
|
|
||||||
keyFingerprint,
|
|
||||||
algorithm: metadata.algorithm,
|
|
||||||
ivPrefix: metadata.iv.substring(0, 8),
|
|
||||||
tagPrefix: metadata.tag.substring(0, 8),
|
|
||||||
dataSize: encryptedData.length,
|
|
||||||
});
|
|
||||||
|
|
||||||
const decipher = crypto.createDecipheriv(
|
const decipher = crypto.createDecipheriv(
|
||||||
metadata.algorithm,
|
metadata.algorithm,
|
||||||
key,
|
key,
|
||||||
@@ -363,14 +285,6 @@ class DatabaseFileEncryption {
|
|||||||
decipher.final(),
|
decipher.final(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
databaseLogger.debug("Database decryption successful", {
|
|
||||||
operation: "database_buffer_decryption_success",
|
|
||||||
encryptedPath,
|
|
||||||
encryptedSize: encryptedData.length,
|
|
||||||
decryptedSize: decryptedBuffer.length,
|
|
||||||
keyFingerprint,
|
|
||||||
});
|
|
||||||
|
|
||||||
return decryptedBuffer;
|
return decryptedBuffer;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
|
|||||||
@@ -51,17 +51,8 @@ class SystemCrypto {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (fileError) {
|
} catch (fileError) {}
|
||||||
databaseLogger.warn("Failed to read .env file for JWT secret", {
|
|
||||||
operation: "jwt_init_file_read_failed",
|
|
||||||
error:
|
|
||||||
fileError instanceof Error ? fileError.message : "Unknown error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
databaseLogger.warn("Generating new JWT secret", {
|
|
||||||
operation: "jwt_generating_new_secret",
|
|
||||||
});
|
|
||||||
await this.generateAndGuideUser();
|
await this.generateAndGuideUser();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
databaseLogger.error("Failed to initialize JWT secret", error, {
|
databaseLogger.error("Failed to initialize JWT secret", error, {
|
||||||
@@ -92,23 +83,9 @@ class SystemCrypto {
|
|||||||
.digest("hex")
|
.digest("hex")
|
||||||
.substring(0, 16);
|
.substring(0, 16);
|
||||||
|
|
||||||
databaseLogger.info("DATABASE_KEY loaded from environment variable", {
|
|
||||||
operation: "db_key_loaded_from_env",
|
|
||||||
keyFingerprint,
|
|
||||||
keyLength: envKey.length,
|
|
||||||
dataDir,
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
databaseLogger.debug(
|
|
||||||
"DATABASE_KEY not found in environment, checking .env file",
|
|
||||||
{
|
|
||||||
operation: "db_key_checking_file",
|
|
||||||
envPath,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const envContent = await fs.readFile(envPath, "utf8");
|
const envContent = await fs.readFile(envPath, "utf8");
|
||||||
const dbKeyMatch = envContent.match(/^DATABASE_KEY=(.+)$/m);
|
const dbKeyMatch = envContent.match(/^DATABASE_KEY=(.+)$/m);
|
||||||
@@ -122,34 +99,10 @@ class SystemCrypto {
|
|||||||
.digest("hex")
|
.digest("hex")
|
||||||
.substring(0, 16);
|
.substring(0, 16);
|
||||||
|
|
||||||
databaseLogger.info("DATABASE_KEY loaded from .env file", {
|
|
||||||
operation: "db_key_loaded_from_file",
|
|
||||||
keyFingerprint,
|
|
||||||
keyLength: dbKeyMatch[1].length,
|
|
||||||
envPath,
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
databaseLogger.warn(
|
|
||||||
"DATABASE_KEY found in .env but invalid or too short",
|
|
||||||
{
|
|
||||||
operation: "db_key_invalid_in_file",
|
|
||||||
envPath,
|
|
||||||
hasMatch: !!dbKeyMatch,
|
|
||||||
keyLength: dbKeyMatch?.[1]?.length || 0,
|
|
||||||
requiredLength: 64,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (fileError) {
|
|
||||||
databaseLogger.warn("Failed to read .env file for DATABASE_KEY", {
|
|
||||||
operation: "db_key_file_read_failed",
|
|
||||||
envPath,
|
|
||||||
error:
|
|
||||||
fileError instanceof Error ? fileError.message : "Unknown error",
|
|
||||||
willGenerateNew: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
} catch (fileError) {}
|
||||||
|
|
||||||
await this.generateAndGuideDatabaseKey();
|
await this.generateAndGuideDatabaseKey();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -24,11 +24,20 @@ import {
|
|||||||
verifyTOTPLogin,
|
verifyTOTPLogin,
|
||||||
getServerConfig,
|
getServerConfig,
|
||||||
isElectron,
|
isElectron,
|
||||||
logoutUser,
|
|
||||||
} from "../../main-axios.ts";
|
} from "../../main-axios.ts";
|
||||||
import { ElectronServerConfig as ServerConfigComponent } from "@/ui/desktop/authentication/ElectronServerConfig.tsx";
|
import { ElectronServerConfig as ServerConfigComponent } from "@/ui/desktop/authentication/ElectronServerConfig.tsx";
|
||||||
import { ElectronLoginForm } from "@/ui/desktop/authentication/ElectronLoginForm.tsx";
|
import { ElectronLoginForm } from "@/ui/desktop/authentication/ElectronLoginForm.tsx";
|
||||||
|
|
||||||
|
function getCookie(name: string): string | undefined {
|
||||||
|
const value = `; ${document.cookie}`;
|
||||||
|
const parts = value.split(`; ${name}=`);
|
||||||
|
if (parts.length === 2) return parts.pop()?.split(";").shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExtendedWindow extends Window {
|
||||||
|
IS_ELECTRON_WEBVIEW?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
interface AuthProps extends React.ComponentProps<"div"> {
|
interface AuthProps extends React.ComponentProps<"div"> {
|
||||||
setLoggedIn: (loggedIn: boolean) => void;
|
setLoggedIn: (loggedIn: boolean) => void;
|
||||||
setIsAdmin: (isAdmin: boolean) => void;
|
setIsAdmin: (isAdmin: boolean) => void;
|
||||||
@@ -37,7 +46,6 @@ interface AuthProps extends React.ComponentProps<"div"> {
|
|||||||
loggedIn: boolean;
|
loggedIn: boolean;
|
||||||
authLoading: boolean;
|
authLoading: boolean;
|
||||||
setDbError: (error: string | null) => void;
|
setDbError: (error: string | null) => void;
|
||||||
dbError?: string | null;
|
|
||||||
onAuthSuccess: (authData: {
|
onAuthSuccess: (authData: {
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
username: string | null;
|
username: string | null;
|
||||||
@@ -54,21 +62,20 @@ export function Auth({
|
|||||||
loggedIn,
|
loggedIn,
|
||||||
authLoading,
|
authLoading,
|
||||||
setDbError,
|
setDbError,
|
||||||
dbError,
|
|
||||||
onAuthSuccess,
|
onAuthSuccess,
|
||||||
...props
|
...props
|
||||||
}: AuthProps) {
|
}: AuthProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const isInElectronWebView = () => {
|
const isInElectronWebView = () => {
|
||||||
if ((window as any).IS_ELECTRON_WEBVIEW) {
|
if ((window as ExtendedWindow).IS_ELECTRON_WEBVIEW) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (window.self !== window.top) {
|
if (window.self !== window.top) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (_e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -126,7 +133,7 @@ export function Auth({
|
|||||||
userId: meRes.userId || null,
|
userId: meRes.userId || null,
|
||||||
});
|
});
|
||||||
toast.success(t("messages.loginSuccess"));
|
toast.success(t("messages.loginSuccess"));
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
toast.error(t("errors.failedUserInfo"));
|
toast.error(t("errors.failedUserInfo"));
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
@@ -206,7 +213,7 @@ export function Auth({
|
|||||||
.finally(() => {
|
.finally(() => {
|
||||||
setDbHealthChecking(false);
|
setDbHealthChecking(false);
|
||||||
});
|
});
|
||||||
}, [setDbError, firstUserToastShown, showServerConfig]);
|
}, [setDbError, firstUserToastShown, showServerConfig, t]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!registrationAllowed && !internalLoggedIn) {
|
if (!registrationAllowed && !internalLoggedIn) {
|
||||||
@@ -282,9 +289,9 @@ export function Auth({
|
|||||||
);
|
);
|
||||||
setWebviewAuthSuccess(true);
|
setWebviewAuthSuccess(true);
|
||||||
setTimeout(() => window.location.reload(), 100);
|
setTimeout(() => window.location.reload(), 100);
|
||||||
setLoading(false);
|
} catch (e) {
|
||||||
return;
|
console.error("Error posting auth success message:", e);
|
||||||
} catch (e) {}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [meRes] = await Promise.all([getUserInfo()]);
|
const [meRes] = await Promise.all([getUserInfo()]);
|
||||||
@@ -461,7 +468,9 @@ export function Auth({
|
|||||||
setTimeout(() => window.location.reload(), 100);
|
setTimeout(() => window.location.reload(), 100);
|
||||||
setTotpLoading(false);
|
setTotpLoading(false);
|
||||||
return;
|
return;
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
console.error("Error posting auth success message:", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setInternalLoggedIn(true);
|
setInternalLoggedIn(true);
|
||||||
@@ -569,7 +578,9 @@ export function Auth({
|
|||||||
setTimeout(() => window.location.reload(), 100);
|
setTimeout(() => window.location.reload(), 100);
|
||||||
setOidcLoading(false);
|
setOidcLoading(false);
|
||||||
return;
|
return;
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
console.error("Error posting auth success message:", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,7 +618,16 @@ export function Auth({
|
|||||||
setOidcLoading(false);
|
setOidcLoading(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, []);
|
}, [
|
||||||
|
onAuthSuccess,
|
||||||
|
setDbError,
|
||||||
|
setIsAdmin,
|
||||||
|
setLoggedIn,
|
||||||
|
setUserId,
|
||||||
|
setUsername,
|
||||||
|
t,
|
||||||
|
isInElectronWebView,
|
||||||
|
]);
|
||||||
|
|
||||||
const Spinner = (
|
const Spinner = (
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
Reference in New Issue
Block a user