Skip to content

Commit ee79a54

Browse files
committed
redo the auth so that only one user acc is created
1 parent 52ad922 commit ee79a54

File tree

6 files changed

+118
-69
lines changed

6 files changed

+118
-69
lines changed

apps/obsidian/src/components/AdminPanelSettings.tsx

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,57 @@
1-
import { useState } from "react";
1+
import { useState, useCallback } from "react";
22
import { usePlugin } from "./PluginContext";
33
import { Notice } from "obsidian";
4+
import { initializeSupabaseSync } from "~/utils/syncDgNodesToSupabase";
45

56
export const AdminPanelSettings = () => {
67
const plugin = usePlugin();
8+
const [syncModeEnabled, setSyncModeEnabled] = useState<boolean>(
9+
plugin.settings.syncModeEnabled ?? false,
10+
);
711
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
812

13+
const handleSyncModeToggle = useCallback((newValue: boolean) => {
14+
setSyncModeEnabled(newValue);
15+
setHasUnsavedChanges(true);
16+
}, []);
17+
918
const handleSave = async () => {
19+
plugin.settings.syncModeEnabled = syncModeEnabled;
1020
await plugin.saveSettings();
1121
new Notice("Admin panel settings saved");
1222
setHasUnsavedChanges(false);
23+
24+
if (syncModeEnabled) {
25+
try {
26+
await initializeSupabaseSync(plugin);
27+
new Notice("Sync mode initialized successfully");
28+
} catch (error) {
29+
console.error("Failed to initialize sync mode:", error);
30+
new Notice(
31+
`Failed to initialize sync mode: ${error instanceof Error ? error.message : String(error)}`,
32+
);
33+
}
34+
}
1335
};
1436

1537
return (
1638
<div className="general-settings">
17-
{/* Add more admin panel settings sections here */}
39+
<div className="setting-item">
40+
<div className="setting-item-info">
41+
<div className="setting-item-name">(BETA) Sync mode enable</div>
42+
<div className="setting-item-description">
43+
Enable synchronization with Discourse Graph database
44+
</div>
45+
</div>
46+
<div className="setting-item-control">
47+
<div
48+
className={`checkbox-container ${syncModeEnabled ? "is-enabled" : ""}`}
49+
onClick={() => handleSyncModeToggle(!syncModeEnabled)}
50+
>
51+
<input type="checkbox" checked={syncModeEnabled} />
52+
</div>
53+
</div>
54+
</div>
1855

1956
<div className="setting-item">
2057
<button

apps/obsidian/src/components/GeneralSettings.tsx

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { usePlugin } from "./PluginContext";
33
import { Notice, setIcon } from "obsidian";
44
import SuggestInput from "./SuggestInput";
55
import { SLACK_LOGO, WHITE_LOGO_SVG } from "~/icons";
6-
import { initializeSupabaseSync } from "~/utils/syncDgNodesToSupabase";
76

87
const DOCS_URL = "https://discoursegraphs.com/docs/obsidian";
98
const COMMUNITY_URL =
@@ -158,9 +157,6 @@ const GeneralSettings = () => {
158157
const [nodeTagHotkey, setNodeTagHotkey] = useState<string>(
159158
plugin.settings.nodeTagHotkey,
160159
);
161-
const [syncModeEnabled, setSyncModeEnabled] = useState<boolean>(
162-
plugin.settings.syncModeEnabled ?? false,
163-
);
164160
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
165161

166162
const handleToggleChange = (newValue: boolean) => {
@@ -194,11 +190,6 @@ const GeneralSettings = () => {
194190
}
195191
}, []);
196192

197-
const handleSyncModeToggle = useCallback((newValue: boolean) => {
198-
setSyncModeEnabled(newValue);
199-
setHasUnsavedChanges(true);
200-
}, []);
201-
202193
const handleSave = async () => {
203194
const trimmedNodesFolderPath = nodesFolderPath.trim();
204195
const trimmedCanvasFolderPath = canvasFolderPath.trim();
@@ -210,25 +201,12 @@ const GeneralSettings = () => {
210201
plugin.settings.canvasAttachmentsFolderPath =
211202
trimmedCanvasAttachmentsFolderPath;
212203
plugin.settings.nodeTagHotkey = nodeTagHotkey || "";
213-
plugin.settings.syncModeEnabled = syncModeEnabled;
214204
setNodesFolderPath(trimmedNodesFolderPath);
215205
setCanvasFolderPath(trimmedCanvasFolderPath);
216206
setCanvasAttachmentsFolderPath(trimmedCanvasAttachmentsFolderPath);
217207
await plugin.saveSettings();
218208
new Notice("General settings saved");
219209
setHasUnsavedChanges(false);
220-
221-
if (syncModeEnabled) {
222-
try {
223-
await initializeSupabaseSync(plugin);
224-
new Notice("Sync mode initialized successfully");
225-
} catch (error) {
226-
console.error("Failed to initialize sync mode:", error);
227-
new Notice(
228-
`Failed to initialize sync mode: ${error instanceof Error ? error.message : String(error)}`,
229-
);
230-
}
231-
}
232210
};
233211

234212
return (
@@ -333,23 +311,6 @@ const GeneralSettings = () => {
333311
</div>
334312
</div>
335313

336-
<div className="setting-item">
337-
<div className="setting-item-info">
338-
<div className="setting-item-name">(BETA) Sync mode enable</div>
339-
<div className="setting-item-description">
340-
Enable synchronization with Discourse Graph database
341-
</div>
342-
</div>
343-
<div className="setting-item-control">
344-
<div
345-
className={`checkbox-container ${syncModeEnabled ? "is-enabled" : ""}`}
346-
onClick={() => handleSyncModeToggle(!syncModeEnabled)}
347-
>
348-
<input type="checkbox" checked={syncModeEnabled} />
349-
</div>
350-
</div>
351-
</div>
352-
353314
<div className="setting-item">
354315
<button
355316
onClick={() => void handleSave()}

apps/obsidian/src/utils/supabaseContext.ts

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ export type SupabaseContext = {
1919
let contextCache: SupabaseContext | null = null;
2020

2121
const generateAccountLocalId = (vaultName: string): string => {
22-
const randomSuffix = Math.random()
23-
.toString(36)
24-
.substring(2, 8)
25-
.toUpperCase();
26-
return `${vaultName}-${randomSuffix}`;
22+
const randomSuffix = Math.random().toString(36).substring(2, 8).toUpperCase();
23+
const sanitizedVaultName = vaultName
24+
.replace(/\s+/g, "")
25+
.replace(/[^a-zA-Z0-9]/g, "")
26+
.replace(/-+/g, "-");
27+
return `${sanitizedVaultName}${randomSuffix}`;
2728
};
2829

2930
const getOrCreateSpacePassword = async (
@@ -32,7 +33,6 @@ const getOrCreateSpacePassword = async (
3233
if (plugin.settings.spacePassword) {
3334
return plugin.settings.spacePassword;
3435
}
35-
// Generate UUID using crypto.randomUUID()
3636
const password = crypto.randomUUID();
3737
plugin.settings.spacePassword = password;
3838
await plugin.saveSettings();
@@ -52,18 +52,30 @@ const getOrCreateAccountLocalId = async (
5252
return accountLocalId;
5353
};
5454

55+
/**
56+
* Gets the unique vault ID from Obsidian's internal API.
57+
* @see https://help.obsidian.md/Extending+Obsidian/Obsidian+URI
58+
*/
59+
const getVaultId = (app: DiscourseGraphPlugin["app"]): string => {
60+
return (app as unknown as { appId: string }).appId;
61+
};
62+
63+
const canonicalObsidianUrl = (vaultId: string): string => {
64+
return `obsidian:${vaultId}`;
65+
};
66+
5567
export const getSupabaseContext = async (
5668
plugin: DiscourseGraphPlugin,
5769
): Promise<SupabaseContext | null> => {
5870
if (contextCache === null) {
5971
try {
6072
const vaultName = plugin.app.vault.getName() || "obsidian-vault";
73+
const vaultId = getVaultId(plugin.app);
6174

6275
const spacePassword = await getOrCreateSpacePassword(plugin);
6376
const accountLocalId = await getOrCreateAccountLocalId(plugin, vaultName);
6477

65-
// Space URL format: "space" + accountLocalId
66-
const url = `space${accountLocalId}`;
78+
const url = canonicalObsidianUrl(vaultId);
6779
const platform: Platform = "Obsidian";
6880

6981
const spaceResult = await fetchOrCreateSpaceDirect({
@@ -83,7 +95,7 @@ export const getSupabaseContext = async (
8395
platform: "Obsidian",
8496
accountLocalId,
8597
name: vaultName,
86-
email: undefined,
98+
email: accountLocalId,
8799
spaceId,
88100
password: spacePassword,
89101
});
@@ -109,17 +121,47 @@ export const getLoggedInClient = async (
109121
): Promise<DGSupabaseClient | null> => {
110122
if (loggedInClient === null) {
111123
const context = await getSupabaseContext(plugin);
112-
if (context === null) throw new Error("Could not create context");
113-
loggedInClient = await createLoggedInClient(
114-
context.platform,
115-
context.spaceId,
116-
context.spacePassword,
117-
);
124+
if (context === null) {
125+
throw new Error("Could not create Supabase context");
126+
}
127+
try {
128+
loggedInClient = await createLoggedInClient({
129+
platform: context.platform,
130+
spaceId: context.spaceId,
131+
password: context.spacePassword,
132+
});
133+
if (!loggedInClient) {
134+
throw new Error(
135+
"Failed to create Supabase client - check environment variables",
136+
);
137+
}
138+
} catch (error) {
139+
const errorMessage =
140+
error instanceof Error ? error.message : String(error);
141+
console.error("Failed to create logged-in client:", errorMessage);
142+
throw new Error(`Supabase authentication failed: ${errorMessage}`);
143+
}
118144
} else {
119145
// renew session
120146
const { error } = await loggedInClient.auth.getSession();
121147
if (error) {
148+
console.warn("Session renewal failed, re-authenticating:", error);
122149
loggedInClient = null;
150+
const context = await getSupabaseContext(plugin);
151+
if (context === null) {
152+
throw new Error(
153+
"Could not create Supabase context for re-authentication",
154+
);
155+
}
156+
157+
loggedInClient = await createLoggedInClient({
158+
platform: context.platform,
159+
spaceId: context.spaceId,
160+
password: context.spacePassword,
161+
});
162+
if (!loggedInClient) {
163+
throw new Error("Failed to re-authenticate Supabase client");
164+
}
123165
}
124166
}
125167
return loggedInClient;

apps/roam/src/utils/supabaseContext.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,11 @@ export const getLoggedInClient = async (): Promise<DGSupabaseClient | null> => {
9696
if (_loggedInClient === null) {
9797
const context = await getSupabaseContext();
9898
if (context === null) throw new Error("Could not create context");
99-
_loggedInClient = await createLoggedInClient(
100-
context.platform,
101-
context.spaceId,
102-
context.spacePassword,
103-
);
99+
_loggedInClient = await createLoggedInClient({
100+
platform: context.platform,
101+
spaceId: context.spaceId,
102+
password: context.spacePassword,
103+
});
104104
} else {
105105
// renew session
106106
const { error } = await _loggedInClient.auth.getSession();

packages/database/src/lib/contextFunctions.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,20 @@ export const fetchOrCreateSpaceDirect = async (
123123
};
124124
};
125125

126-
export const createLoggedInClient = async (
127-
platform: Platform,
128-
spaceId: number,
129-
password: string,
130-
): Promise<DGSupabaseClient | null> => {
126+
export const createLoggedInClient = async ({
127+
platform,
128+
spaceId,
129+
password,
130+
}: {
131+
platform: Platform;
132+
spaceId: number;
133+
password: string;
134+
}): Promise<DGSupabaseClient | null> => {
131135
const loggedInClient: DGSupabaseClient | null = createClient();
132136
if (!loggedInClient) return null;
137+
const email = spaceAnonUserEmail(platform, spaceId);
133138
const { error } = await loggedInClient.auth.signInWithPassword({
134-
email: spaceAnonUserEmail(platform, spaceId),
139+
email,
135140
password: password,
136141
});
137142
if (error) {
@@ -155,7 +160,11 @@ export const fetchOrCreatePlatformAccount = async ({
155160
spaceId: number;
156161
password: string;
157162
}): Promise<number> => {
158-
const supabase = await createLoggedInClient(platform, spaceId, password);
163+
const supabase = await createLoggedInClient({
164+
platform,
165+
spaceId,
166+
password,
167+
});
159168
if (!supabase) throw Error("Missing database connection");
160169

161170
const result = await supabase.rpc("create_account_in_space", {

packages/database/supabase/functions/create-space/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,4 +260,4 @@ Deno.serve(async (req) => {
260260
--header 'Content-Type: application/json' \
261261
--data '{ "url":"https://roamresearch.com/#/app/abc", "name":"abc","platform":"Roam", "password": "abcdefgh" }'
262262
263-
*/
263+
*/

0 commit comments

Comments
 (0)