Neon / portal template support (#713)

TODOs:
- [x] Do restart when checkout / restore if there is a DB
- [x] List all branches (branch id, name, date)
- [x] Allow checking out versions with no DB
- [x] safeguard to never delete main branches
- [x] create app hook for neon template
- [x] weird UX with connector on configure panel
- [x] tiny neon logo in connector
- [x] deploy to vercel
- [x] build forgot password page
- [x] what about email setup
- [x] lots of imgix errors
- [x] edit file - db snapshot
- [x] DYAD_DISABLE_DB_PUSH
- [ ] update portal doc
- [x] switch preview branch to be read-only endpoint
- [x] disable supabase sys prompt if neon is enabled
- [ ] https://payloadcms.com/docs/upload/storage-adapters
- [x] need to use main branch...

Phase 2?
- [x] generate DB migrations
This commit is contained in:
Will Chen
2025-08-04 16:36:09 -07:00
committed by GitHub
parent 0f1a5c5c77
commit b0f08eaf15
50 changed files with 3525 additions and 205 deletions

View File

@@ -11,6 +11,7 @@ import {
} from "@/atoms/appAtoms";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { AppOutput } from "@/ipc/ipc_types";
import { showInputRequest } from "@/lib/toast";
const useRunAppLoadingAtom = atom(false);
@@ -18,7 +19,7 @@ export function useRunApp() {
const [loading, setLoading] = useAtom(useRunAppLoadingAtom);
const [app, setApp] = useAtom(currentAppAtom);
const setAppOutput = useSetAtom(appOutputAtom);
const [appUrlObj, setAppUrlObj] = useAtom(appUrlAtom);
const [, setAppUrlObj] = useAtom(appUrlAtom);
const setPreviewPanelKey = useSetAtom(previewPanelKeyAtom);
const appId = useAtomValue(selectedAppIdAtom);
const setPreviewErrorMessage = useSetAtom(previewErrorMessageAtom);
@@ -39,47 +40,78 @@ export function useRunApp() {
const originalUrl = originalUrlMatch && originalUrlMatch[1];
setAppUrlObj({
appUrl: proxyUrl,
appId: appId!,
appId: output.appId,
originalUrl: originalUrl!,
});
}
}
};
const runApp = useCallback(async (appId: number) => {
setLoading(true);
try {
const ipcClient = IpcClient.getInstance();
console.debug("Running app", appId);
// Clear the URL and add restart message
if (appUrlObj?.appId !== appId) {
setAppUrlObj({ appUrl: null, appId: null, originalUrl: null });
const processAppOutput = useCallback(
(output: AppOutput) => {
// Handle input requests specially
if (output.type === "input-requested") {
showInputRequest(output.message, async (response) => {
try {
const ipcClient = IpcClient.getInstance();
await ipcClient.respondToAppInput({
appId: output.appId,
response,
});
} catch (error) {
console.error("Failed to respond to app input:", error);
}
});
return; // Don't add to regular output
}
setAppOutput((prev) => [
...prev,
{
message: "Trying to restart app...",
type: "stdout",
appId,
timestamp: Date.now(),
},
]);
const app = await ipcClient.getApp(appId);
setApp(app);
await ipcClient.runApp(appId, (output) => {
setAppOutput((prev) => [...prev, output]);
processProxyServerOutput(output);
});
setPreviewErrorMessage(undefined);
} catch (error) {
console.error(`Error running app ${appId}:`, error);
setPreviewErrorMessage(
error instanceof Error ? error.message : error?.toString(),
);
} finally {
setLoading(false);
}
}, []);
// Add to regular app output
setAppOutput((prev) => [...prev, output]);
// Process proxy server output
processProxyServerOutput(output);
},
[setAppOutput],
);
const runApp = useCallback(
async (appId: number) => {
setLoading(true);
try {
const ipcClient = IpcClient.getInstance();
console.debug("Running app", appId);
// Clear the URL and add restart message
setAppUrlObj((prevAppUrlObj) => {
if (prevAppUrlObj?.appId !== appId) {
return { appUrl: null, appId: null, originalUrl: null };
}
return prevAppUrlObj; // No change needed
});
setAppOutput((prev) => [
...prev,
{
message: "Trying to restart app...",
type: "stdout",
appId,
timestamp: Date.now(),
},
]);
const app = await ipcClient.getApp(appId);
setApp(app);
await ipcClient.runApp(appId, processAppOutput);
setPreviewErrorMessage(undefined);
} catch (error) {
console.error(`Error running app ${appId}:`, error);
setPreviewErrorMessage(
error instanceof Error ? error.message : error?.toString(),
);
} finally {
setLoading(false);
}
},
[processAppOutput],
);
const stopApp = useCallback(async (appId: number) => {
if (appId === null) {
@@ -139,15 +171,15 @@ export function useRunApp() {
await ipcClient.restartApp(
appId,
(output) => {
setAppOutput((prev) => [...prev, output]);
// Handle HMR updates before processing
if (
output.message.includes("hmr update") &&
output.message.includes("[vite]")
) {
onHotModuleReload();
return;
}
processProxyServerOutput(output);
// Process normally (including input requests)
processAppOutput(output);
},
removeNodeModules,
);
@@ -161,7 +193,15 @@ export function useRunApp() {
setLoading(false);
}
},
[appId, setApp, setAppOutput, setAppUrlObj, setPreviewPanelKey],
[
appId,
setApp,
setAppOutput,
setAppUrlObj,
setPreviewPanelKey,
processAppOutput,
onHotModuleReload,
],
);
const refreshAppIframe = useCallback(async () => {