Merge branch 'dev'
CI/CD / deploy (push) Successful in 2m59s
Details
CI/CD / deploy (push) Successful in 2m59s
Details
This commit is contained in:
commit
609bae45f5
|
|
@ -2892,7 +2892,7 @@ export const socialsModule: RSpaceModule = {
|
|||
acceptsFeeds: ["data", "trust"],
|
||||
outputPaths: [
|
||||
{ path: "campaigns", name: "Campaigns", icon: "📢", description: "Social media campaigns" },
|
||||
{ path: "posts", name: "Posts", icon: "📱", description: "Social feed posts across platforms" },
|
||||
{ path: "threads", name: "Posts", icon: "💬", description: "Draft posts and tweet-thread builder with live preview" },
|
||||
],
|
||||
subPageInfos: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -365,12 +365,36 @@ spaces.get("/:slug/modules", async (c) => {
|
|||
const doc = getDocumentData(slug);
|
||||
if (!doc?.meta) return c.json({ error: "Space not found" }, 404);
|
||||
|
||||
// Caller identity: owner sees raw secrets, everyone else gets password fields redacted
|
||||
let isOwner = false;
|
||||
const token = extractToken(c.req.raw.headers);
|
||||
if (token) {
|
||||
try {
|
||||
const claims = await verifyToken(token);
|
||||
if (doc.meta.ownerDID && claims.sub === doc.meta.ownerDID) isOwner = true;
|
||||
} catch { /* invalid token → treat as anonymous */ }
|
||||
}
|
||||
|
||||
const allModules = getAllModules();
|
||||
const enabled = doc.meta.enabledModules; // null = all
|
||||
const overrides = doc.meta.moduleScopeOverrides || {};
|
||||
|
||||
const savedSettings = doc.meta.moduleSettings || {};
|
||||
|
||||
const redactSettings = (modId: string, settings: Record<string, string | boolean>) => {
|
||||
if (isOwner) return settings;
|
||||
const mod = getModule(modId);
|
||||
const passwordKeys = new Set(
|
||||
(mod?.settingsSchema ?? []).filter(f => f.type === 'password').map(f => f.key),
|
||||
);
|
||||
if (passwordKeys.size === 0) return settings;
|
||||
const redacted: Record<string, string | boolean> = {};
|
||||
for (const [k, v] of Object.entries(settings)) {
|
||||
redacted[k] = passwordKeys.has(k) && typeof v === 'string' && v.length > 0 ? '********' : v;
|
||||
}
|
||||
return redacted;
|
||||
};
|
||||
|
||||
const modules = allModules.map(mod => ({
|
||||
id: mod.id,
|
||||
name: mod.name,
|
||||
|
|
@ -382,7 +406,7 @@ spaces.get("/:slug/modules", async (c) => {
|
|||
currentScope: overrides[mod.id] || mod.scoping.defaultScope,
|
||||
},
|
||||
...(mod.settingsSchema ? { settingsSchema: mod.settingsSchema } : {}),
|
||||
...(savedSettings[mod.id] ? { settings: savedSettings[mod.id] } : {}),
|
||||
...(savedSettings[mod.id] ? { settings: redactSettings(mod.id, savedSettings[mod.id]) } : {}),
|
||||
}));
|
||||
|
||||
return c.json({ modules, enabledModules: enabled });
|
||||
|
|
@ -453,10 +477,17 @@ spaces.patch("/:slug/modules", async (c) => {
|
|||
if (!mod) return c.json({ error: `Unknown module: ${modId}` }, 400);
|
||||
if (!mod.settingsSchema) return c.json({ error: `Module ${modId} has no settings schema` }, 400);
|
||||
const validKeys = new Set(mod.settingsSchema.map(f => f.key));
|
||||
const passwordKeys = new Set(mod.settingsSchema.filter(f => f.type === 'password').map(f => f.key));
|
||||
for (const key of Object.keys(settings)) {
|
||||
if (!validKeys.has(key)) return c.json({ error: `Unknown setting '${key}' for module ${modId}` }, 400);
|
||||
}
|
||||
merged[modId] = { ...(existing[modId] || {}), ...settings };
|
||||
// Preserve existing value when password fields come back as the redaction sentinel
|
||||
const incoming: Record<string, string | boolean> = {};
|
||||
for (const [k, v] of Object.entries(settings)) {
|
||||
if (passwordKeys.has(k) && v === '********') continue;
|
||||
incoming[k] = v;
|
||||
}
|
||||
merged[modId] = { ...(existing[modId] || {}), ...incoming };
|
||||
}
|
||||
updates.moduleSettings = merged;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue