Skip to content

Commit c605d90

Browse files
update method publish
1 parent 54df509 commit c605d90

File tree

10 files changed

+450
-118
lines changed

10 files changed

+450
-118
lines changed

apps/web/client/src/env.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const env = createEnv({
1010
NODE_ENV: z.enum(['development', 'test', 'production']),
1111
CSB_API_KEY: z.string(),
1212
SUPABASE_DATABASE_URL: z.string().url(),
13+
SUPABASE_SERVICE_ROLE_KEY: z.string().optional(),
1314
RESEND_API_KEY: z.string().optional(),
1415
FREESTYLE_API_KEY: z.string().optional(),
1516

@@ -71,6 +72,7 @@ export const env = createEnv({
7172

7273
// Supabase
7374
SUPABASE_DATABASE_URL: process.env.SUPABASE_DATABASE_URL,
75+
SUPABASE_SERVICE_ROLE_KEY: process.env.SUPABASE_SERVICE_ROLE_KEY,
7476
NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL,
7577
NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
7678
NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { FreestyleDeployWebSuccessResponseV2 } from 'freestyle-sandboxes';
1+
import type { DeploymentSource, FreestyleDeployWebSuccessResponseV2 } from 'freestyle-sandboxes';
22
import { initializeFreestyleSdk } from '../freestyle';
33
import type {
44
HostingProviderAdapter,
@@ -9,34 +9,62 @@ import type {
99
export class FreestyleAdapter implements HostingProviderAdapter {
1010
async deploy(request: DeploymentRequest): Promise<DeploymentResponse> {
1111
const sdk = initializeFreestyleSdk();
12-
12+
if (request.sourceUrl) {
13+
// Many SDKs accept a generic URL-based source. Use a loose cast to avoid type mismatch.
14+
15+
const res = await sdk.deployWeb(
16+
{ kind: 'tar', url: request.sourceUrl } as unknown as DeploymentSource,
17+
request.config,
18+
);
19+
20+
const freestyleResponse = res as {
21+
message?: string;
22+
error?: { message: string };
23+
data?: FreestyleDeployWebSuccessResponseV2;
24+
};
25+
26+
if (freestyleResponse.error) {
27+
throw new Error(
28+
freestyleResponse.error.message ||
29+
freestyleResponse.message ??
30+
'Unknown error',
31+
);
32+
}
33+
34+
return {
35+
deploymentId: freestyleResponse.data?.deploymentId ?? '',
36+
success: true,
37+
message: freestyleResponse.message,
38+
};
39+
}
40+
1341
const res = await sdk.deployWeb(
1442
{
1543
files: request.files,
1644
kind: 'files',
1745
},
18-
request.config
46+
request.config,
1947
);
20-
48+
2149
const freestyleResponse = res as {
2250
message?: string;
2351
error?: {
2452
message: string;
2553
};
2654
data?: FreestyleDeployWebSuccessResponseV2;
2755
};
28-
56+
2957
if (freestyleResponse.error) {
3058
throw new Error(
31-
freestyleResponse.error.message ||
32-
freestyleResponse.message ||
33-
'Unknown error'
59+
freestyleResponse.error.message ||
60+
freestyleResponse.message ||
61+
'Unknown error',
3462
);
3563
}
36-
64+
3765
return {
3866
deploymentId: freestyleResponse.data?.deploymentId ?? '',
39-
success: true
67+
success: true,
4068
};
4169
}
4270
}

apps/web/client/src/server/api/routers/publish/deployment.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { z } from "zod";
99
import { createTRPCRouter, protectedProcedure } from "../../trpc";
1010
import { updateDeployment } from './helpers';
1111
import { createDeployment, publish } from './helpers/index.ts';
12+
import { getDeploymentLogs } from './helpers/logs';
1213

1314
export const deploymentRouter = createTRPCRouter({
1415
getByType: protectedProcedure.input(z.object({
@@ -121,6 +122,11 @@ export const deploymentRouter = createTRPCRouter({
121122
throw error;
122123
}
123124
}),
125+
getLogs: protectedProcedure.input(z.object({
126+
deploymentId: z.string(),
127+
})).query(async ({ input }) => {
128+
return getDeploymentLogs(input.deploymentId);
129+
}),
124130
cancel: protectedProcedure.input(z.object({
125131
deploymentId: z.string(),
126132
})).mutation(async ({ ctx, input }) => {

apps/web/client/src/server/api/routers/publish/helpers/deploy.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,39 @@ import {
1414
import { HostingProviderFactory } from '../../domain/hosting-factory.ts';
1515

1616
export const deployFreestyle = async (
17-
{
18-
files,
19-
urls,
20-
envVars,
21-
}: {
22-
files: Record<string, FreestyleFile>,
23-
urls: string[],
24-
envVars?: Record<string, string>,
17+
args: (
18+
{ files: Record<string, FreestyleFile>; sourceUrl?: undefined } |
19+
{ files?: undefined; sourceUrl: string }
20+
) & {
21+
urls: string[];
22+
envVars?: Record<string, string>;
2523
}
2624
): Promise<{
2725
success: boolean;
2826
message?: string;
2927
}> => {
3028
const entrypoint = 'server.js';
3129
const adapter = HostingProviderFactory.create(HostingProvider.FREESTYLE);
32-
const deploymentFiles: Record<string, { content: string; encoding?: 'utf-8' | 'base64' }> = {};
33-
for (const [path, file] of Object.entries(files)) {
34-
deploymentFiles[path] = {
35-
content: file.content,
36-
encoding: (file.encoding === 'base64' ? 'base64' : 'utf-8')
37-
};
38-
}
3930

4031
const result = await adapter.deploy({
41-
files: deploymentFiles,
32+
...(('sourceUrl' in args && typeof args.sourceUrl === 'string')
33+
? { sourceUrl: args.sourceUrl }
34+
: {
35+
files: Object.fromEntries(
36+
Object.entries(args.files ?? {}).map(([path, file]) => [
37+
path,
38+
{
39+
content: file.content,
40+
encoding: (file.encoding === 'base64' ? 'base64' : 'utf-8'),
41+
} as const,
42+
]),
43+
),
44+
}
45+
),
4246
config: {
43-
domains: urls,
47+
domains: args.urls,
4448
entrypoint,
45-
envVars,
49+
envVars: args.envVars,
4650
},
4751
});
4852

apps/web/client/src/server/api/routers/publish/helpers/env.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { WebSocketSession } from '@codesandbox/sdk';
1+
import type { WebSocketSession } from '@codesandbox/sdk';
22

33
/**
44
* Parse .env file content into key-value pairs
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
export type DeploymentLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'success';
2+
3+
export type DeploymentLogEntry = {
4+
timestamp: number;
5+
level: DeploymentLogLevel;
6+
message: string;
7+
};
8+
9+
const MAX_ENTRIES_PER_DEPLOYMENT = 500;
10+
11+
const deploymentLogs = new Map<string, DeploymentLogEntry[]>();
12+
13+
export const addDeploymentLog = (
14+
deploymentId: string,
15+
message: string,
16+
level: DeploymentLogLevel = 'info',
17+
): void => {
18+
if (!deploymentId) return;
19+
const entry: DeploymentLogEntry = {
20+
timestamp: Date.now(),
21+
level,
22+
message,
23+
};
24+
const list = deploymentLogs.get(deploymentId) ?? [];
25+
list.push(entry);
26+
// Trim to max
27+
if (list.length > MAX_ENTRIES_PER_DEPLOYMENT) {
28+
const excess = list.length - MAX_ENTRIES_PER_DEPLOYMENT;
29+
list.splice(0, excess);
30+
}
31+
deploymentLogs.set(deploymentId, list);
32+
};
33+
34+
export const getDeploymentLogs = (deploymentId: string): DeploymentLogEntry[] => {
35+
if (!deploymentId) return [];
36+
return deploymentLogs.get(deploymentId) ?? [];
37+
};
38+
39+
export const clearDeploymentLogs = (deploymentId: string): void => {
40+
if (!deploymentId) return;
41+
deploymentLogs.delete(deploymentId);
42+
};
43+
44+

apps/web/client/src/server/api/routers/publish/helpers/publish.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { deployFreestyle } from './deploy';
1111
import { extractEnvVarsFromSandbox } from './env';
1212
import { forkBuildSandbox } from './fork';
1313
import { getProjectUrls, getSandboxId, updateDeployment } from './helpers';
14+
import { addDeploymentLog, clearDeploymentLogs } from './logs';
1415

1516
export async function publish({
1617
db,
@@ -21,8 +22,12 @@ export async function publish({
2122
}) {
2223
const { id: deploymentId, projectId, type, buildScript, buildFlags, envVars, requestedBy: userId } = deployment;
2324
try {
25+
clearDeploymentLogs(deploymentId);
26+
addDeploymentLog(deploymentId, 'Starting deployment...', 'info');
2427
const deploymentUrls = await getProjectUrls(db, projectId, type);
28+
addDeploymentLog(deploymentId, `Resolved deployment URLs: ${deploymentUrls.join(', ')}`, 'debug');
2529
const sandboxId = await getSandboxId(db, projectId);
30+
addDeploymentLog(deploymentId, `Using sandbox ${sandboxId} for build`, 'debug');
2631

2732
const updateDeploymentResult1 = await updateDeployment(db, deploymentId, {
2833
status: DeploymentStatus.IN_PROGRESS,
@@ -37,6 +42,7 @@ export async function publish({
3742
}
3843

3944
const { session, sandboxId: forkedSandboxId } = await forkBuildSandbox(sandboxId, userId, deploymentId);
45+
addDeploymentLog(deploymentId, `Forked build sandbox: ${forkedSandboxId}`, 'info');
4046

4147
try {
4248
const updateDeploymentResult2 = await updateDeployment(db, deploymentId, {
@@ -54,12 +60,15 @@ export async function publish({
5460

5561

5662
const publishManager = new PublishManager(session);
57-
const files = await publishManager.publish({
63+
addDeploymentLog(deploymentId, 'Building project inside sandbox...', 'info');
64+
const artifactUrl = await publishManager.buildAndUploadArtifact({
5865
skipBadge: type === DeploymentType.CUSTOM,
5966
buildScript: buildScript ?? DefaultSettings.COMMANDS.build,
6067
buildFlags: buildFlags ?? DefaultSettings.EDITOR_SETTINGS.buildFlags,
68+
deploymentId,
6169
updateDeployment: (deployment) => updateDeployment(db, deploymentId, deployment),
6270
});
71+
addDeploymentLog(deploymentId, `Build artifact uploaded. Signed URL generated.`, 'success');
6372

6473
const updateDeploymentResult3 = await updateDeployment(db, deploymentId, {
6574
status: DeploymentStatus.IN_PROGRESS,
@@ -75,15 +84,19 @@ export async function publish({
7584

7685
// Note: Prefer user provided env vars over sandbox env vars
7786
const sandboxEnvVars = await extractEnvVarsFromSandbox(session);
87+
addDeploymentLog(deploymentId, `Extracted ${Object.keys(sandboxEnvVars).length} env vars from sandbox`, 'debug');
7888
const mergedEnvVars = { ...sandboxEnvVars, ...(envVars ?? {}) };
89+
addDeploymentLog(deploymentId, 'Starting deployment to hosting provider...', 'info');
7990

8091
await deployFreestyle({
81-
files,
92+
sourceUrl: artifactUrl,
8293
urls: deploymentUrls,
8394
envVars: mergedEnvVars,
8495
});
96+
addDeploymentLog(deploymentId, 'Deployment finalized by hosting provider', 'success');
8597
} finally {
8698
await session.disconnect();
99+
addDeploymentLog(deploymentId, 'Build sandbox disconnected', 'debug');
87100
}
88101
} catch (error) {
89102
console.error(error);
@@ -92,6 +105,11 @@ export async function publish({
92105
error: error instanceof Error ? error.message : 'Unknown error',
93106
progress: 100,
94107
});
108+
addDeploymentLog(
109+
deploymentId,
110+
error instanceof Error ? error.message : 'Unknown error during deployment',
111+
'error',
112+
);
95113
throw error;
96114
}
97115
}

0 commit comments

Comments
 (0)