Skip to content

Commit 801c1f4

Browse files
committed
isolate asset syncing, to avoid syncing twice
1 parent 3fec3af commit 801c1f4

2 files changed

Lines changed: 125 additions & 79 deletions

File tree

apps/obsidian/src/utils/publishNode.ts

Lines changed: 13 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import type { FrontMatterCache, TFile } from "obsidian";
2-
import { Notice } from "obsidian";
32
import type { default as DiscourseGraphPlugin } from "~/index";
43
import { getLoggedInClient, getSupabaseContext } from "./supabaseContext";
5-
import { addFile } from "@repo/database/lib/files";
6-
import mime from "mime-types";
74
import type { DGSupabaseClient } from "@repo/database/lib/client";
85
import {
96
getRelationsForNodeInstanceId,
@@ -15,7 +12,10 @@ import {
1512
} from "./relationsStore";
1613
import type { RelationInstance } from "~/types";
1714
import { getAvailableGroupIds } from "./importNodes";
18-
import { syncAllNodesAndRelations } from "./syncDgNodesToSupabase";
15+
import {
16+
syncAllNodesAndRelations,
17+
syncPublishedNodeAssets,
18+
} from "./syncDgNodesToSupabase";
1919
import type { DiscourseNodeInVault } from "./getDiscourseNodes";
2020
import type { SupabaseContext } from "./supabaseContext";
2121
import type { TablesInsert } from "@repo/database/dbTypes";
@@ -518,65 +518,15 @@ export const publishNodeToGroup = async ({
518518
});
519519
}
520520
}
521-
522-
// Always sync non-text assets when node is published to this group
523-
const existingFiles: string[] = [];
524-
const existingReferencesReq = await client
525-
.from("my_file_references")
526-
.select("*")
527-
.eq("space_id", spaceId)
528-
.eq("source_local_id", nodeId);
529-
if (existingReferencesReq.error) {
530-
console.error(existingReferencesReq.error);
531-
return;
532-
}
533-
const existingReferencesByPath = Object.fromEntries(
534-
existingReferencesReq.data.map((ref) => [ref.filepath, ref]),
535-
);
536-
537-
for (const attachment of attachments) {
538-
const mimetype = mime.lookup(attachment.path) || "application/octet-stream";
539-
if (mimetype.startsWith("text/")) continue;
540-
// Do not use standard upload for large files
541-
if (attachment.stat.size >= 6 * 1024 * 1024) {
542-
new Notice(
543-
`Asset file ${attachment.path} is larger than 6Mb and will not be uploaded`,
544-
);
545-
continue;
546-
}
547-
existingFiles.push(attachment.path);
548-
const existingRef = existingReferencesByPath[attachment.path];
549-
if (
550-
!existingRef ||
551-
new Date(existingRef.last_modified + "Z").valueOf() <
552-
attachment.stat.mtime
553-
) {
554-
const content = await plugin.app.vault.readBinary(attachment);
555-
await addFile({
556-
client,
557-
spaceId,
558-
sourceLocalId: nodeId,
559-
fname: attachment.path,
560-
mimetype,
561-
created: new Date(attachment.stat.ctime),
562-
lastModified: new Date(attachment.stat.mtime),
563-
content,
564-
});
565-
}
566-
}
567-
let cleanupCommand = client
568-
.from("FileReference")
569-
.delete()
570-
.eq("space_id", spaceId)
571-
.eq("source_local_id", nodeId);
572-
if (existingFiles.length)
573-
cleanupCommand = cleanupCommand.notIn("filepath", [
574-
...new Set(existingFiles),
575-
]);
576-
const cleanupResult = await cleanupCommand;
577-
// do not fail on cleanup
578-
if (cleanupResult.error) console.error(cleanupResult.error);
579-
521+
if (!republish)
522+
await syncPublishedNodeAssets({
523+
plugin,
524+
client,
525+
nodeId,
526+
spaceId,
527+
file,
528+
attachments,
529+
});
580530
if (!existingPublish.includes(myGroup))
581531
await plugin.app.fileManager.processFrontMatter(
582532
file,

apps/obsidian/src/utils/syncDgNodesToSupabase.ts

Lines changed: 112 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
/* eslint-disable @typescript-eslint/naming-convention */
2-
import { FrontMatterCache, Notice, TFile } from "obsidian";
2+
import { Notice, TFile } from "obsidian";
3+
import { addFile } from "@repo/database/lib/files";
4+
import mime from "mime-types";
35
import { ensureNodeInstanceId } from "~/utils/nodeInstanceId";
46
import type { DGSupabaseClient } from "@repo/database/lib/client";
57
import type { Json } from "@repo/database/dbTypes";
@@ -9,7 +11,7 @@ import {
911
type SupabaseContext,
1012
} from "./supabaseContext";
1113
import { default as DiscourseGraphPlugin } from "~/index";
12-
import { publishNode, ensurePublishedRelationsAccuracy } from "./publishNode";
14+
import { ensurePublishedRelationsAccuracy } from "./publishNode";
1315
import { upsertNodesToSupabaseAsContentWithEmbeddings } from "./upsertNodesAsContentWithEmbeddings";
1416
import {
1517
orderConceptsByDependency,
@@ -579,6 +581,92 @@ const convertDgToSupabaseConcepts = async ({
579581
}
580582
};
581583

584+
export const syncPublishedNodeAssets = async ({
585+
plugin,
586+
client,
587+
nodeId,
588+
spaceId,
589+
file,
590+
attachments,
591+
}: {
592+
plugin: DiscourseGraphPlugin;
593+
client: DGSupabaseClient;
594+
nodeId: string;
595+
spaceId: number;
596+
file: TFile;
597+
attachments?: TFile[];
598+
}) => {
599+
if (attachments === undefined) {
600+
const embeds = plugin.app.metadataCache.getFileCache(file)?.embeds ?? [];
601+
attachments = embeds
602+
.map(({ link }) => {
603+
const attachment = plugin.app.metadataCache.getFirstLinkpathDest(
604+
link,
605+
file.path,
606+
);
607+
return attachment;
608+
})
609+
.filter((a) => !!a);
610+
}
611+
// Always sync non-text assets when node is published to this group
612+
const existingFiles: string[] = [];
613+
const existingReferencesReq = await client
614+
.from("my_file_references")
615+
.select("*")
616+
.eq("space_id", spaceId)
617+
.eq("source_local_id", nodeId);
618+
if (existingReferencesReq.error) {
619+
console.error(existingReferencesReq.error);
620+
return;
621+
}
622+
const existingReferencesByPath = Object.fromEntries(
623+
existingReferencesReq.data.map((ref) => [ref.filepath, ref]),
624+
);
625+
626+
for (const attachment of attachments) {
627+
const mimetype = mime.lookup(attachment.path) || "application/octet-stream";
628+
if (mimetype.startsWith("text/")) continue;
629+
// Do not use standard upload for large files
630+
if (attachment.stat.size >= 6 * 1024 * 1024) {
631+
new Notice(
632+
`Asset file ${attachment.path} is larger than 6Mb and will not be uploaded`,
633+
);
634+
continue;
635+
}
636+
existingFiles.push(attachment.path);
637+
const existingRef = existingReferencesByPath[attachment.path];
638+
if (
639+
!existingRef ||
640+
new Date(existingRef.last_modified + "Z").valueOf() <
641+
attachment.stat.mtime
642+
) {
643+
const content = await plugin.app.vault.readBinary(attachment);
644+
await addFile({
645+
client,
646+
spaceId,
647+
sourceLocalId: nodeId,
648+
fname: attachment.path,
649+
mimetype,
650+
created: new Date(attachment.stat.ctime),
651+
lastModified: new Date(attachment.stat.mtime),
652+
content,
653+
});
654+
}
655+
}
656+
let cleanupCommand = client
657+
.from("FileReference")
658+
.delete()
659+
.eq("space_id", spaceId)
660+
.eq("source_local_id", nodeId);
661+
if (existingFiles.length)
662+
cleanupCommand = cleanupCommand.notIn("filepath", [
663+
...new Set(existingFiles),
664+
]);
665+
const cleanupResult = await cleanupCommand;
666+
// do not fail on cleanup
667+
if (cleanupResult.error) console.error(cleanupResult.error);
668+
};
669+
582670
/**
583671
* For nodes that are already published, ensure non-text assets are pushed to
584672
* storage. Called after content sync so new embeds (e.g. images) get uploaded.
@@ -587,25 +675,26 @@ const syncPublishedNodesAssets = async (
587675
plugin: DiscourseGraphPlugin,
588676
nodes: ObsidianDiscourseNodeData[],
589677
): Promise<void> => {
678+
const context = await getSupabaseContext(plugin);
679+
if (!context) throw new Error("Cannot get context");
680+
const spaceId = context.spaceId;
681+
const client = await getLoggedInClient(plugin);
682+
if (!client) throw new Error("Cannot get client");
590683
const published = nodes.filter(
591684
(n) =>
592685
((n.frontmatter.publishedToGroups as string[] | undefined)?.length ?? 0) >
593686
0,
594687
);
595688
for (const node of published) {
596-
try {
597-
await publishNode({
598-
plugin,
599-
file: node.file,
600-
frontmatter: node.frontmatter as FrontMatterCache,
601-
republish: true,
602-
});
603-
} catch (error) {
604-
console.error(
605-
`Failed to sync published node assets for ${node.file.path}:`,
606-
error,
607-
);
608-
}
689+
const nodeId = node.frontmatter.nodeInstanceId as string | undefined;
690+
if (!nodeId) throw new Error("Please sync the node first");
691+
await syncPublishedNodeAssets({
692+
plugin,
693+
client,
694+
nodeId,
695+
spaceId,
696+
file: node.file,
697+
});
609698
}
610699
};
611700

@@ -652,7 +741,14 @@ const syncChangedNodesToSupabase = async ({
652741

653742
// When file changes affect an already-published node, ensure new non-text
654743
// assets (e.g. images) are pushed to storage.
655-
await syncPublishedNodesAssets(plugin, changedNodes);
744+
try {
745+
await syncPublishedNodesAssets(plugin, changedNodes);
746+
} catch (error) {
747+
// console.error(
748+
// `Failed to sync published node assets for ${node.file.path}:`,
749+
// error,
750+
// );
751+
}
656752
};
657753

658754
/**

0 commit comments

Comments
 (0)