mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
feat: improve template fetching with fallback mechanisms
- Implement zip method as primary approach for fetching repo contents with Cloudflare as fallback - Add direct GitHub API fallback when proxy API fails in template selection This provides a more reliable experience especially when using electron
This commit is contained in:
parent
ef1acec0ed
commit
de3acbd415
@ -1,19 +1,6 @@
|
||||
import { json } from '@remix-run/cloudflare';
|
||||
import JSZip from 'jszip';
|
||||
|
||||
// Function to detect if we're running in Cloudflare
|
||||
function isCloudflareEnvironment(context: any): boolean {
|
||||
// Check if we're in production AND have Cloudflare Pages specific env vars
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const hasCfPagesVars = !!(
|
||||
context?.cloudflare?.env?.CF_PAGES ||
|
||||
context?.cloudflare?.env?.CF_PAGES_URL ||
|
||||
context?.cloudflare?.env?.CF_PAGES_COMMIT_SHA
|
||||
);
|
||||
|
||||
return isProduction && hasCfPagesVars;
|
||||
}
|
||||
|
||||
// Cloudflare-compatible method using GitHub Contents API
|
||||
async function fetchRepoContentsCloudflare(repo: string, githubToken?: string) {
|
||||
const baseUrl = 'https://api.github.com';
|
||||
@ -215,10 +202,14 @@ export async function loader({ request, context }: { request: Request; context:
|
||||
|
||||
let fileList;
|
||||
|
||||
if (isCloudflareEnvironment(context)) {
|
||||
fileList = await fetchRepoContentsCloudflare(repo, githubToken);
|
||||
} else {
|
||||
try {
|
||||
// Try zip method first (uses latest release)
|
||||
fileList = await fetchRepoContentsZip(repo, githubToken);
|
||||
} catch (zipError) {
|
||||
console.warn('Zip method failed, falling back to Cloudflare method:', zipError);
|
||||
|
||||
// Fallback to Cloudflare method (uses GitHub Contents API)
|
||||
fileList = await fetchRepoContentsCloudflare(repo, githubToken);
|
||||
}
|
||||
|
||||
// Filter out .git files for both methods
|
||||
|
@ -1,6 +1,7 @@
|
||||
import ignore from 'ignore';
|
||||
import type { ProviderInfo } from '~/shared/types/model';
|
||||
import type { Template } from '~/shared/types/template';
|
||||
import Cookies from 'js-cookie';
|
||||
import { STARTER_TEMPLATES } from './constants';
|
||||
|
||||
const starterTemplateSelectionPrompt = (templates: Template[]) => `
|
||||
@ -114,7 +115,7 @@ export const selectStarterTemplate = async (options: { message: string; model: s
|
||||
|
||||
const getGitHubRepoContent = async (repoName: string): Promise<{ name: string; path: string; content: string }[]> => {
|
||||
try {
|
||||
// Instead of directly fetching from GitHub, use our own API endpoint as a proxy
|
||||
// First try: Use our own API endpoint as a proxy
|
||||
const response = await fetch(`/api/github-template?repo=${encodeURIComponent(repoName)}`);
|
||||
|
||||
if (!response.ok) {
|
||||
@ -126,7 +127,90 @@ const getGitHubRepoContent = async (repoName: string): Promise<{ name: string; p
|
||||
|
||||
return files;
|
||||
} catch (error) {
|
||||
console.error('Error fetching release contents:', error);
|
||||
console.error('Error fetching release contents via API route:', error);
|
||||
console.log('Falling back to direct GitHub API call...');
|
||||
|
||||
// Fallback: Direct GitHub API call
|
||||
return await getGitHubRepoContentDirect(repoName);
|
||||
}
|
||||
};
|
||||
|
||||
// Fallback method for direct GitHub API access
|
||||
const getGitHubRepoContentDirect = async (
|
||||
repoName: string,
|
||||
path: string = '',
|
||||
): Promise<{ name: string; path: string; content: string }[]> => {
|
||||
const baseUrl = 'https://api.github.com';
|
||||
|
||||
try {
|
||||
const token = Cookies.get('githubToken') || import.meta.env.VITE_GITHUB_ACCESS_TOKEN;
|
||||
|
||||
const headers: HeadersInit = {
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
};
|
||||
|
||||
// Add your GitHub token if needed
|
||||
if (token) {
|
||||
headers.Authorization = 'Bearer ' + token;
|
||||
}
|
||||
|
||||
// Fetch contents of the path
|
||||
const response = await fetch(`${baseUrl}/repos/${repoName}/contents/${path}`, {
|
||||
headers,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data: any = await response.json();
|
||||
|
||||
// If it's a single file, return its content
|
||||
if (!Array.isArray(data)) {
|
||||
if (data.type === 'file') {
|
||||
// If it's a file, get its content
|
||||
const content = atob(data.content); // Decode base64 content
|
||||
return [
|
||||
{
|
||||
name: data.name,
|
||||
path: data.path,
|
||||
content,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Process directory contents recursively
|
||||
const contents = await Promise.all(
|
||||
data.map(async (item: any) => {
|
||||
if (item.type === 'dir') {
|
||||
// Recursively get contents of subdirectories
|
||||
return await getGitHubRepoContentDirect(repoName, item.path);
|
||||
} else if (item.type === 'file') {
|
||||
// Fetch file content
|
||||
const fileResponse = await fetch(item.url, {
|
||||
headers,
|
||||
});
|
||||
const fileData: any = await fileResponse.json();
|
||||
const content = atob(fileData.content); // Decode base64 content
|
||||
|
||||
return [
|
||||
{
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
content,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}),
|
||||
);
|
||||
|
||||
// Flatten the array of contents
|
||||
return contents.flat();
|
||||
} catch (error) {
|
||||
console.error('Error fetching repo contents directly from GitHub:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user