WIP: Migrate to using taskfile #187
10 changed files with 92 additions and 98 deletions
Lint
commit
e3a90833ff
|
|
@ -74,7 +74,6 @@ export class Database {
|
||||||
this.documents.forEach((doc) => {
|
this.documents.forEach((doc) => {
|
||||||
this.lastSeenUpdateIds.add(doc.metadata?.parentVersionId);
|
this.lastSeenUpdateIds.add(doc.metadata?.parentVersionId);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get length(): number {
|
public get length(): number {
|
||||||
|
|
@ -104,7 +103,7 @@ export class Database {
|
||||||
i === 0
|
i === 0
|
||||||
? false
|
? false
|
||||||
: records[i - 1].parallelVersion ===
|
: records[i - 1].parallelVersion ===
|
||||||
current.parallelVersion
|
current.parallelVersion
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -282,7 +281,6 @@ export class Database {
|
||||||
candidate.isDeleted = true;
|
candidate.isDeleted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public getLastSeenUpdateId(): VaultUpdateId {
|
public getLastSeenUpdateId(): VaultUpdateId {
|
||||||
return this.lastSeenUpdateIds.min;
|
return this.lastSeenUpdateIds.min;
|
||||||
}
|
}
|
||||||
|
|
@ -317,7 +315,7 @@ export class Database {
|
||||||
...metadata! // `resolvedDocuments` only returns docs with metadata set
|
...metadata! // `resolvedDocuments` only returns docs with metadata set
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
lastSeenUpdateId: this.lastSeenUpdateIds.min,
|
lastSeenUpdateId: this.lastSeenUpdateIds.min
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -341,7 +339,7 @@ export class Database {
|
||||||
if (duplicates.length > 0) {
|
if (duplicates.length > 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Document IDs are not unique, found duplicates: " +
|
"Document IDs are not unique, found duplicates: " +
|
||||||
duplicates.join("; ")
|
duplicates.join("; ")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,8 @@ export class SyncService {
|
||||||
(await response.json()) as DocumentUpdateResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
|
(await response.json()) as DocumentUpdateResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Updated document ${JSON.stringify(result)} with id ${result.documentId
|
`Updated document ${JSON.stringify(result)} with id ${
|
||||||
|
result.documentId
|
||||||
}}`
|
}}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -209,7 +210,8 @@ export class SyncService {
|
||||||
(await response.json()) as DocumentUpdateResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
|
(await response.json()) as DocumentUpdateResponse; // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Updated document ${JSON.stringify(result)} with id ${result.documentId
|
`Updated document ${JSON.stringify(result)} with id ${
|
||||||
|
result.documentId
|
||||||
}}`
|
}}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -336,7 +338,7 @@ export class SyncService {
|
||||||
return this.retryForever(async () => {
|
return this.retryForever(async () => {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
"Getting all documents" +
|
"Getting all documents" +
|
||||||
(since != null ? ` since ${since}` : "")
|
(since != null ? ` since ${since}` : "")
|
||||||
);
|
);
|
||||||
|
|
||||||
const url = new URL(this.getUrl("/documents"));
|
const url = new URL(this.getUrl("/documents"));
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ export class WebSocketManager {
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
private readonly settings: Settings,
|
private readonly settings: Settings,
|
||||||
private readonly webSocketFactoryImplementation: typeof globalThis.WebSocket = WebSocket
|
private readonly webSocketFactoryImplementation: typeof globalThis.WebSocket = WebSocket
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
public get isWebSocketConnected(): boolean {
|
public get isWebSocketConnected(): boolean {
|
||||||
return (
|
return (
|
||||||
|
|
@ -260,10 +260,9 @@ export class WebSocketManager {
|
||||||
this.resolveDisconnectingPromise?.();
|
this.resolveDisconnectingPromise?.();
|
||||||
this.resolveDisconnectingPromise = null;
|
this.resolveDisconnectingPromise = null;
|
||||||
} else {
|
} else {
|
||||||
const delay = this.settings.getSettings().webSocketRetryIntervalMs;
|
const delay =
|
||||||
this.logger.info(
|
this.settings.getSettings().webSocketRetryIntervalMs;
|
||||||
`Reconnecting to WebSocket in ${delay}ms...`
|
this.logger.info(`Reconnecting to WebSocket in ${delay}ms...`);
|
||||||
);
|
|
||||||
this.reconnectTimeoutId = setTimeout(() => {
|
this.reconnectTimeoutId = setTimeout(() => {
|
||||||
this.reconnectTimeoutId = undefined;
|
this.reconnectTimeoutId = undefined;
|
||||||
this.initializeWebSocket();
|
this.initializeWebSocket();
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ export class SyncClient {
|
||||||
database: Partial<StoredDatabase>;
|
database: Partial<StoredDatabase>;
|
||||||
}>
|
}>
|
||||||
>
|
>
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
public get documentCount(): number {
|
public get documentCount(): number {
|
||||||
return this.database.length;
|
return this.database.length;
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ export class Syncer {
|
||||||
// in that case, we mustn't move it again.
|
// in that case, we mustn't move it again.
|
||||||
if (
|
if (
|
||||||
this.database.getLatestDocumentByRelativePath(relativePath) ===
|
this.database.getLatestDocumentByRelativePath(relativePath) ===
|
||||||
undefined ||
|
undefined ||
|
||||||
this.database.getLatestDocumentByRelativePath(relativePath)
|
this.database.getLatestDocumentByRelativePath(relativePath)
|
||||||
?.isDeleted === true
|
?.isDeleted === true
|
||||||
) {
|
) {
|
||||||
|
|
@ -484,6 +484,5 @@ export class Syncer {
|
||||||
return this.syncLocallyDeletedFile(relativePath);
|
return this.syncLocallyDeletedFile(relativePath);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ export class UnrestrictedSyncer {
|
||||||
forceMerge: true
|
forceMerge: true
|
||||||
});
|
});
|
||||||
|
|
||||||
this.handleMaybeMergingResponse({
|
await this.handleMaybeMergingResponse({
|
||||||
document,
|
document,
|
||||||
response,
|
response,
|
||||||
contentHash,
|
contentHash,
|
||||||
|
|
@ -159,14 +159,14 @@ export class UnrestrictedSyncer {
|
||||||
const updateDetails: SyncUpdateDetails | SyncMovedDetails =
|
const updateDetails: SyncUpdateDetails | SyncMovedDetails =
|
||||||
oldPath !== undefined
|
oldPath !== undefined
|
||||||
? {
|
? {
|
||||||
type: SyncType.MOVE,
|
type: SyncType.MOVE,
|
||||||
relativePath: document.relativePath,
|
relativePath: document.relativePath,
|
||||||
movedFrom: oldPath
|
movedFrom: oldPath
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
type: SyncType.UPDATE,
|
type: SyncType.UPDATE,
|
||||||
relativePath: document.relativePath
|
relativePath: document.relativePath
|
||||||
};
|
};
|
||||||
|
|
||||||
await this.executeSync(updateDetails, async () => {
|
await this.executeSync(updateDetails, async () => {
|
||||||
const originalRelativePath = document.relativePath;
|
const originalRelativePath = document.relativePath;
|
||||||
|
|
@ -181,7 +181,7 @@ export class UnrestrictedSyncer {
|
||||||
const contentBytes = await this.operations.read(
|
const contentBytes = await this.operations.read(
|
||||||
document.relativePath
|
document.relativePath
|
||||||
); // this can throw FileNotFoundError
|
); // this can throw FileNotFoundError
|
||||||
let contentHash = hash(contentBytes);
|
const contentHash = hash(contentBytes);
|
||||||
|
|
||||||
const areThereLocalChanges = !(
|
const areThereLocalChanges = !(
|
||||||
document.metadata.hash === contentHash && oldPath === undefined
|
document.metadata.hash === contentHash && oldPath === undefined
|
||||||
|
|
@ -205,22 +205,22 @@ export class UnrestrictedSyncer {
|
||||||
response =
|
response =
|
||||||
isText && cachedVersion !== undefined
|
isText && cachedVersion !== undefined
|
||||||
? await this.syncService.putText({
|
? await this.syncService.putText({
|
||||||
documentId: document.metadata.documentId,
|
documentId: document.metadata.documentId,
|
||||||
parentVersionId:
|
parentVersionId:
|
||||||
document.metadata.parentVersionId,
|
document.metadata.parentVersionId,
|
||||||
relativePath: document.relativePath,
|
relativePath: document.relativePath,
|
||||||
content: diff(
|
content: diff(
|
||||||
new TextDecoder().decode(cachedVersion),
|
new TextDecoder().decode(cachedVersion),
|
||||||
new TextDecoder().decode(contentBytes)
|
new TextDecoder().decode(contentBytes)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
: await this.syncService.putBinary({
|
: await this.syncService.putBinary({
|
||||||
documentId: document.metadata.documentId,
|
documentId: document.metadata.documentId,
|
||||||
parentVersionId:
|
parentVersionId:
|
||||||
document.metadata.parentVersionId,
|
document.metadata.parentVersionId,
|
||||||
relativePath: document.relativePath,
|
relativePath: document.relativePath,
|
||||||
contentBytes
|
contentBytes
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (!force) {
|
if (!force) {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
|
|
@ -234,10 +234,9 @@ export class UnrestrictedSyncer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.handleMaybeMergingResponse({
|
||||||
this.handleMaybeMergingResponse({
|
|
||||||
document,
|
document,
|
||||||
response: response!,
|
response: response,
|
||||||
contentHash,
|
contentHash,
|
||||||
originalRelativePath,
|
originalRelativePath,
|
||||||
originalContentBytes: contentBytes
|
originalContentBytes: contentBytes
|
||||||
|
|
@ -255,16 +254,16 @@ export class UnrestrictedSyncer {
|
||||||
|
|
||||||
const actualUpdateDetails: SyncUpdateDetails | SyncMovedDetails =
|
const actualUpdateDetails: SyncUpdateDetails | SyncMovedDetails =
|
||||||
oldPath !== undefined ||
|
oldPath !== undefined ||
|
||||||
response.relativePath != originalRelativePath
|
response.relativePath != originalRelativePath
|
||||||
? {
|
? {
|
||||||
type: SyncType.MOVE,
|
type: SyncType.MOVE,
|
||||||
relativePath: response.relativePath,
|
relativePath: response.relativePath,
|
||||||
movedFrom: oldPath ?? originalRelativePath
|
movedFrom: oldPath ?? originalRelativePath
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
type: SyncType.UPDATE,
|
type: SyncType.UPDATE,
|
||||||
relativePath: response.relativePath
|
relativePath: response.relativePath
|
||||||
};
|
};
|
||||||
|
|
||||||
if (areThereLocalChanges) {
|
if (areThereLocalChanges) {
|
||||||
this.history.addHistoryEntry({
|
this.history.addHistoryEntry({
|
||||||
|
|
@ -288,7 +287,8 @@ export class UnrestrictedSyncer {
|
||||||
type: SyncType.DELETE,
|
type: SyncType.DELETE,
|
||||||
relativePath: document.relativePath
|
relativePath: document.relativePath
|
||||||
},
|
},
|
||||||
message: "File has been deleted remotely, so we deleted it locally",
|
message:
|
||||||
|
"File has been deleted remotely, so we deleted it locally",
|
||||||
author: response.userId,
|
author: response.userId,
|
||||||
timestamp: new Date(response.updatedDate)
|
timestamp: new Date(response.updatedDate)
|
||||||
});
|
});
|
||||||
|
|
@ -460,24 +460,21 @@ export class UnrestrictedSyncer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleMaybeMergingResponse(
|
private async handleMaybeMergingResponse({
|
||||||
{
|
document,
|
||||||
document,
|
response,
|
||||||
response,
|
contentHash,
|
||||||
contentHash,
|
originalRelativePath,
|
||||||
originalRelativePath,
|
originalContentBytes
|
||||||
originalContentBytes
|
}: {
|
||||||
}: {
|
document: DocumentRecord;
|
||||||
document: DocumentRecord;
|
response: DocumentVersion | DocumentUpdateResponse;
|
||||||
response: DocumentVersion | DocumentUpdateResponse,
|
contentHash: string;
|
||||||
contentHash: string,
|
originalRelativePath: string;
|
||||||
originalRelativePath: string,
|
originalContentBytes: Uint8Array;
|
||||||
originalContentBytes: Uint8Array
|
}): Promise<void> {
|
||||||
}
|
|
||||||
): Promise<void> {
|
|
||||||
|
|
||||||
// `document` is mutable and reflects the latest state in the local database
|
// `document` is mutable and reflects the latest state in the local database
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
||||||
if (document.isDeleted) {
|
if (document.isDeleted) {
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
`Document ${document.relativePath} has been deleted before we could finish updating it`
|
`Document ${document.relativePath} has been deleted before we could finish updating it`
|
||||||
|
|
@ -572,8 +569,9 @@ export class UnrestrictedSyncer {
|
||||||
type: SyncType.SKIPPED,
|
type: SyncType.SKIPPED,
|
||||||
relativePath
|
relativePath
|
||||||
},
|
},
|
||||||
message: `File size of ${sizeInMB} MB exceeds the maximum file size limit of ${maxFileSizeMB
|
message: `File size of ${sizeInMB} MB exceeds the maximum file size limit of ${
|
||||||
} MB`
|
maxFileSizeMB
|
||||||
|
} MB`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -598,8 +596,6 @@ export class UnrestrictedSyncer {
|
||||||
document: DocumentRecord,
|
document: DocumentRecord,
|
||||||
response: DocumentVersion | DocumentUpdateResponse
|
response: DocumentVersion | DocumentUpdateResponse
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
|
||||||
|
|
||||||
this.database.delete(document.relativePath);
|
this.database.delete(document.relativePath);
|
||||||
this.database.updateDocumentMetadata(
|
this.database.updateDocumentMetadata(
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -102,13 +102,13 @@ export class MockClient implements FileSystemOperations {
|
||||||
.map((part) => part.trim());
|
.map((part) => part.trim());
|
||||||
const newParts = newContent.split(" ").map((part) => part.trim());
|
const newParts = newContent.split(" ").map((part) => part.trim());
|
||||||
existingParts.forEach((part) =>
|
existingParts.forEach((part) =>
|
||||||
// all changes should be additive
|
// all changes should be additive
|
||||||
{
|
{
|
||||||
assert(
|
assert(
|
||||||
newParts.includes(part),
|
newParts.includes(part),
|
||||||
`Part ${part} not found in new content: ${newContent}`
|
`Part ${part} not found in new content: ${newContent}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,24 +9,24 @@ server:
|
||||||
max_clients_per_vault: 256
|
max_clients_per_vault: 256
|
||||||
response_timeout: 30m
|
response_timeout: 30m
|
||||||
mergeable_file_extensions:
|
mergeable_file_extensions:
|
||||||
- md
|
- md
|
||||||
- txt
|
- txt
|
||||||
users:
|
users:
|
||||||
user_configs:
|
user_configs:
|
||||||
- name: admin
|
- name: admin
|
||||||
token: test-token-change-me
|
token: test-token-change-me
|
||||||
vault_access:
|
vault_access:
|
||||||
type: allow_access_to_all
|
type: allow_access_to_all
|
||||||
- name: other-admin
|
- name: other-admin
|
||||||
token: test-token-change-me2
|
token: test-token-change-me2
|
||||||
vault_access:
|
vault_access:
|
||||||
type: allow_access_to_all
|
type: allow_access_to_all
|
||||||
- name: test
|
- name: test
|
||||||
token: other-test-token
|
token: other-test-token
|
||||||
vault_access:
|
vault_access:
|
||||||
type: allow_list
|
type: allow_list
|
||||||
allowed:
|
allowed:
|
||||||
- default
|
- default
|
||||||
logging:
|
logging:
|
||||||
log_directory: logs
|
log_directory: logs
|
||||||
log_rotation: 7days
|
log_rotation: 7days
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ pub async fn merge_with_stored_version(
|
||||||
};
|
};
|
||||||
|
|
||||||
// We can only update the relative path if we're the first one to do so
|
// We can only update the relative path if we're the first one to do so
|
||||||
let new_relative_path = if parent_document_path == &latest_version.relative_path
|
let new_relative_path = if parent_document_path == latest_version.relative_path
|
||||||
&& latest_version.relative_path != sanitized_relative_path
|
&& latest_version.relative_path != sanitized_relative_path
|
||||||
{
|
{
|
||||||
let new_path = find_first_available_path(
|
let new_path = find_first_available_path(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::app_state::database::models::VaultId;
|
use crate::app_state::database::models::VaultId;
|
||||||
use crate::{app_state::database::Transaction, utils::dedup_paths::dedup_paths};
|
use crate::{app_state::database::Transaction, utils::dedup_paths::dedup_paths};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use log::{debug, info};
|
use log::info;
|
||||||
|
|
||||||
pub async fn find_first_available_path(
|
pub async fn find_first_available_path(
|
||||||
vault_id: &VaultId,
|
vault_id: &VaultId,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue