Skip to content

Commit 81c1c9e

Browse files
hotlongCopilot
andcommitted
fix(cloud): resolve project ownerSeed via sys_user lookup, not session re-probe
The dispatcher already runs a session-resolve block earlier when created_by === '__session__' (line ~1762), which populates req.created_by with the user id. A subsequent getSession() call to fetch user details (email/name/image) was unreliable because the cloud project-create route is often invoked via an internal RPC that does not carry the original browser cookies — so getSession() returned null and ownerSeed was never written into project metadata. Fix: prefer req.created_by (already an authenticated user id at this point) and look up the user row directly via objectql for email/name/ image. Falls back to the legacy session probe when created_by is not available (e.g. CLI scripts that don't use the __session__ placeholder). This unblocks Plan A end-to-end: 1. orgSeed (already shipped) mirrors the owning cloud org. 2. ownerSeed (this commit) replays the owner as sys_user. 3. seedProjectMember binds the owner to the mirrored cloud org, skipping SecurityPlugin's auto-personal-workspace middleware. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7bed32e commit 81c1c9e

1 file changed

Lines changed: 55 additions & 14 deletions

File tree

packages/runtime/src/http-dispatcher.ts

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1873,25 +1873,66 @@ export class HttpDispatcher {
18731873
// identity here — the project DB does not exist yet — and let
18741874
// ArtifactKernelFactory replay the seed once the project's
18751875
// sys_user table is provisioned. Best-effort: failure to
1876-
// resolve the session must not abort project creation.
1876+
// resolve the user must not abort project creation.
1877+
//
1878+
// We prefer `req.created_by` (already resolved upstream from
1879+
// `__session__` or sent explicitly by the cloud frontend) and
1880+
// look the user row up directly via objectql. The previous
1881+
// approach — calling `authService.api.getSession({headers})`
1882+
// a SECOND time — was unreliable because the cloud's project-
1883+
// create route is often invoked via an internal RPC where the
1884+
// original browser cookies do not propagate.
18771885
try {
1878-
const authService: any = await this.getService(CoreServiceName.enum.auth);
1879-
const sessionData = await authService?.api?.getSession?.({
1880-
headers: _context?.request?.headers,
1881-
});
1882-
const u = sessionData?.user;
1883-
if (u?.id && u?.email) {
1886+
let ownerUserId: string | undefined = req.created_by && req.created_by !== 'system'
1887+
? String(req.created_by)
1888+
: undefined;
1889+
let ownerEmail: string | undefined;
1890+
let ownerName: string | undefined;
1891+
let ownerImage: string | undefined;
1892+
1893+
if (ownerUserId) {
1894+
const userRow = await ql.find('sys_user', { where: { id: ownerUserId } } as any);
1895+
const userRows = Array.isArray(userRow) ? userRow : (userRow?.value ?? []);
1896+
const u = Array.isArray(userRows) && userRows.length > 0 ? userRows[0] : null;
1897+
if (u?.email) {
1898+
ownerEmail = String(u.email);
1899+
ownerName = u.name ? String(u.name) : undefined;
1900+
ownerImage = u.image ? String(u.image) : undefined;
1901+
}
1902+
}
1903+
1904+
// Fall back to the session probe if `created_by` was not
1905+
// set (e.g. CLI scripts) — preserves the prior behaviour
1906+
// when cookies are available.
1907+
if (!ownerEmail) {
1908+
try {
1909+
const authService: any = await this.getService(CoreServiceName.enum.auth);
1910+
const sessionData = await authService?.api?.getSession?.({
1911+
headers: _context?.request?.headers,
1912+
});
1913+
const u = sessionData?.user;
1914+
if (u?.id && u?.email) {
1915+
ownerUserId = String(u.id);
1916+
ownerEmail = String(u.email);
1917+
ownerName = u.name ? String(u.name) : undefined;
1918+
ownerImage = u.image ? String(u.image) : undefined;
1919+
}
1920+
} catch {
1921+
// session lookup is best-effort
1922+
}
1923+
}
1924+
1925+
if (ownerUserId && ownerEmail) {
18841926
(baseMetadata as any).ownerSeed = {
1885-
userId: String(u.id),
1886-
email: String(u.email),
1887-
name: u.name ? String(u.name) : null,
1888-
image: u.image ? String(u.image) : null,
1927+
userId: ownerUserId,
1928+
email: ownerEmail,
1929+
name: ownerName ?? null,
1930+
image: ownerImage ?? null,
18891931
};
18901932
}
18911933
} catch {
1892-
// Session unavailable (e.g. service-to-service call) —
1893-
// skip owner seed; later access flows still work via the
1894-
// platform-SSO JIT path.
1934+
// owner lookup failed entirely — skip seed; later access
1935+
// flows still work via the platform-SSO JIT path.
18951936
}
18961937

18971938
// Also capture the OWNING cloud org so the per-project

0 commit comments

Comments
 (0)