Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions apps/roam/src/utils/getBlockProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ export const normalizeProps = (props: json): json =>
)
: props;

export const getRawBlockProps = (uid: string) =>
(window.roamAlphaAPI.pull("[:block/props]", [":block/uid", uid])?.[
":block/props"
] || {}) as Record<string, json>;

const getBlockProps = (uid: string) =>
normalizeProps(
(window.roamAlphaAPI.pull("[:block/props]", [":block/uid", uid])?.[
":block/props"
] || {}) as Record<string, json>,
) as Record<string, json>;
normalizeProps(getRawBlockProps(uid)) as Record<string, json>;

export default getBlockProps;
49 changes: 49 additions & 0 deletions apps/roam/src/utils/setBlockProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { type json, getRawBlockProps } from "./getBlockProps";
import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle";

export const deNormalizeProps = (props: json): json =>
typeof props === "object"
? props === null
? null
: Array.isArray(props)
? props.map(deNormalizeProps)
: Object.fromEntries(
Object.entries(props).map(([k, v]) => [
`:${k}`,
typeof v === "object" && v !== null && !Array.isArray(v)
? deNormalizeProps(v)
: Array.isArray(v)
? v.map(deNormalizeProps)
: v,
]),
)
: props;

const setBlockProps = (
uid: string,
newProps: Record<string, json>,
denormalize: boolean = false,
) => {
const baseProps = getRawBlockProps(uid);
if (typeof baseProps === "object" && !Array.isArray(baseProps)) {
const props = {
...(baseProps || {}),
...(denormalize
? (deNormalizeProps(newProps) as Record<string, json>)
: newProps),
} as Record<string, json>;
window.roamAlphaAPI.data.block.update({ block: { uid, props } });
return props;
}
return baseProps;
};

export const testSetBlockProps = (
title: string,
newProps: Record<string, json>,
) => {
const uid = getPageUidByPageTitle(title);
return uid ? setBlockProps(uid, newProps) : null;
};

export default setBlockProps;
27 changes: 26 additions & 1 deletion apps/roam/src/utils/supabaseContext.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { getNodeEnv } from "roamjs-components/util/env";
import getCurrentUserEmail from "roamjs-components/queries/getCurrentUserEmail";
import getCurrentUserDisplayName from "roamjs-components/queries/getCurrentUserDisplayName";
import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle";
import getRoamUrl from "roamjs-components/dom/getRoamUrl";

import { Database } from "@repo/database/types.gen";
import { DISCOURSE_CONFIG_PAGE_TITLE } from "~/utils/renderNodeConfigPage";
import getBlockProps from "~/utils/getBlockProps";
import setBlockProps from "~/utils/setBlockProps";

declare const crypto: { randomUUID: () => string };

type Platform = Database["public"]["Enums"]["Platform"];

export type SupabaseContext = {
platform: Platform;
spaceId: number;
userId: number;
spacePassword: string;
};

let CONTEXT_CACHE: SupabaseContext | null = null;
Expand All @@ -20,6 +28,22 @@ const base_url =
? "http://localhost:3000/api/supabase"
: "https://discoursegraphs.com/api/supabase";

const settingsConfigPageUid = getPageUidByPageTitle(
DISCOURSE_CONFIG_PAGE_TITLE,
);

const getOrCreateSpacePassword = () => {
const props = getBlockProps(settingsConfigPageUid);
const existing: string | unknown = props["space-user-password"];
if (existing && typeof existing === "string") return existing;
// use a uuid as password, at least cryptographically safe
const password = crypto.randomUUID();
setBlockProps(settingsConfigPageUid, {
"space-user-password": password,
});
return password;
};
Comment on lines +35 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve type safety and consider error handling.

The function has a couple of areas for improvement:

  1. Type narrowing: The existing variable is typed as string | unknown but the code only handles the string case explicitly.
  2. Error handling: No error handling for the setBlockProps operation, which could fail silently.

Consider this improvement:

const getOrCreateSpacePassword = () => {
  const props = getBlockProps(settingsConfigPageUid);
-  const existing: string | unknown = props["space-user-password"];
-  if (existing && typeof existing === "string") return existing;
+  const existing = props["space-user-password"];
+  if (typeof existing === "string" && existing.length > 0) return existing;
  // use a uuid as password, at least cryptographically safe
  const password = crypto.randomUUID();
-  setBlockProps(settingsConfigPageUid, {
-    "space-user-password": password,
-  });
+  try {
+    setBlockProps(settingsConfigPageUid, {
+      "space-user-password": password,
+    });
+  } catch (error) {
+    console.error("Failed to store space password:", error);
+    // Consider whether to throw or handle gracefully
+  }
  return password;
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getOrCreateSpacePassword = () => {
const props = getBlockProps(settingsConfigPageUid);
const existing: string | unknown = props["space-user-password"];
if (existing && typeof existing === "string") return existing;
// use a uuid as password, at least cryptographically safe
const password = crypto.randomUUID();
setBlockProps(settingsConfigPageUid, {
"space-user-password": password,
});
return password;
};
const getOrCreateSpacePassword = () => {
const props = getBlockProps(settingsConfigPageUid);
const existing = props["space-user-password"];
if (typeof existing === "string" && existing.length > 0) return existing;
// use a uuid as password, at least cryptographically safe
const password = crypto.randomUUID();
try {
setBlockProps(settingsConfigPageUid, {
"space-user-password": password,
});
} catch (error) {
console.error("Failed to store space password:", error);
// Consider whether to throw or handle gracefully
}
return password;
};
🤖 Prompt for AI Agents
In apps/roam/src/utils/supabaseContext.ts around lines 35 to 45, improve type
safety by refining the type of the `existing` variable to exclude `unknown` and
explicitly handle non-string cases or absence of the property. Add error
handling around the `setBlockProps` call by wrapping it in a try-catch block to
catch and handle potential failures, such as logging the error or rethrowing it,
to prevent silent failures.


// Note: Some of this will be more typesafe if rewritten with direct supabase access eventually.
// We're going through nextjs until we have settled security.

Expand Down Expand Up @@ -100,12 +124,13 @@ export const getSupabaseContext = async (): Promise<SupabaseContext | null> => {
const accountLocalId = window.roamAlphaAPI.user.uid();
const personEmail = getCurrentUserEmail();
const personName = getCurrentUserDisplayName();
const spacePassword = getOrCreateSpacePassword();
const userId = await fetchOrCreatePlatformAccount({
accountLocalId,
personName,
personEmail,
});
CONTEXT_CACHE = { platform: "Roam", spaceId, userId };
CONTEXT_CACHE = { platform: "Roam", spaceId, userId, spacePassword };
} catch (error) {
console.error(error);
return null;
Expand Down