mirror of
https://github.com/supabase/supabase.git
synced 2026-07-25 00:42:58 -04:00
997203cd64
## What kind of change does this PR introduce? Feature ## What is the current behavior? Functions page on self-hosted differs from Platform ## What is the new behavior? > [!NOTE] > This PR only add readonly operations. Function edit and deploy should be implemented in a future one. Adds the possibility to download and see function code in Self-Host version. <details> <img width="1465" height="944" alt="image" src="https://github.com/user-attachments/assets/4bbf8f5c-3390-4de6-9e8b-8ec9cd59ebad" /> </details> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * API endpoint to stream function files/artifacts as multipart/form-data. * New function file entry type and server-side file listing for functions. * **Improvements** * Edge Functions "Code" navigation item always visible. * Download popover reworked: ZIP download always available; CLI section shown only on supported platforms. * Editor set to read-only and file actions disabled on unsupported environments. * **Editor** * Added JavaScript, TypeScript, and Markdown language modules for the embedded editor. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Charis Lam <26616127+charislam@users.noreply.github.com>
81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import type { Dirent } from 'node:fs'
|
|
import { readdir, stat } from 'node:fs/promises'
|
|
import path from 'node:path'
|
|
import { pathToFileURL } from 'node:url'
|
|
|
|
import { FunctionArtifact, FunctionFileEntry } from './types'
|
|
|
|
export class FileSystemFunctionsArtifactStore {
|
|
constructor(private folderPath: string) {}
|
|
|
|
async getFunctions(): Promise<FunctionArtifact[]> {
|
|
const dirEntries = await readdir(this.folderPath, { withFileTypes: true })
|
|
|
|
const functionsFolders = dirEntries.filter((dir) => dir.isDirectory() && dir.name !== 'main')
|
|
const functionsArtifacts = await Promise.all(
|
|
functionsFolders.map(parseFolderToFunctionArtifact)
|
|
)
|
|
|
|
return functionsArtifacts.filter((f) => f !== undefined)
|
|
}
|
|
|
|
async getFunctionBySlug(slug: string): Promise<FunctionArtifact | undefined> {
|
|
const dirEntries = await readdir(this.folderPath, { withFileTypes: true })
|
|
|
|
const functionFolder = dirEntries.find(
|
|
(dir) => dir.isDirectory() && dir.name !== 'main' && dir.name === slug
|
|
)
|
|
if (!functionFolder) return
|
|
|
|
return parseFolderToFunctionArtifact(functionFolder)
|
|
}
|
|
|
|
async getFileEntriesBySlug(slug: string): Promise<Array<FunctionFileEntry>> {
|
|
if (slug === 'main') return []
|
|
|
|
const functionFolderPath = path.resolve(this.folderPath, slug)
|
|
if (!functionFolderPath.startsWith(path.resolve(this.folderPath) + path.sep)) return []
|
|
|
|
const entries = await readdir(functionFolderPath, {
|
|
recursive: true,
|
|
withFileTypes: true,
|
|
})
|
|
|
|
const fileEntries = await Promise.all(
|
|
entries
|
|
.filter((entry) => entry.isFile())
|
|
.map(async (entry) => {
|
|
const absolutePath = path.join(entry.parentPath, entry.name)
|
|
const fileStat = await stat(absolutePath)
|
|
return {
|
|
absolutePath,
|
|
relativePath: path.relative(functionFolderPath, absolutePath),
|
|
size: fileStat.size,
|
|
}
|
|
})
|
|
)
|
|
|
|
return fileEntries
|
|
}
|
|
}
|
|
|
|
async function parseFolderToFunctionArtifact(
|
|
folder: Dirent
|
|
): Promise<FunctionArtifact | undefined> {
|
|
const folderPath = path.join(folder.parentPath, folder.name)
|
|
const files = await readdir(folderPath, { withFileTypes: true })
|
|
const entrypoint = files.find((file) => file.isFile() && file.name.startsWith('index'))
|
|
|
|
if (!entrypoint) return
|
|
|
|
const entrypointPath = path.join(folderPath, entrypoint.name)
|
|
const entrypointStat = await stat(entrypointPath)
|
|
|
|
return {
|
|
slug: folder.name,
|
|
entrypoint_path: pathToFileURL(entrypointPath).href,
|
|
created_at: entrypointStat.birthtimeMs,
|
|
updated_at: entrypointStat.mtimeMs,
|
|
}
|
|
}
|