Huakun Shen 402208e95a
Feature: license check (#67)
* feat: add required license field to manifest

* chore: add license MIT to all templates

* feat: add license check to jsr and npm validation

* fix: supabase export

split every item into its own subexport for better debugability

* fix: supabase imports

* feat: hide app when escape is pressed

* fix: update package version in api test from 0.0.6 to 0.0.20

* fix: update test for kunkun extension with new version and commit details

* fix: update kunkun extension test to use version 0.0.4

* fix: update kunkun extension test to reflect new version 0.0.20 and updated commit details

* feat: display downloads in extension details

* feat: add downloads display to store
2025-01-18 22:55:43 -05:00

108 lines
2.7 KiB
TypeScript

import type { PostgrestSingleResponse, SupabaseClient } from "@supabase/supabase-js"
import * as v from "valibot"
import type { Database, Tables } from "./database.types"
import { SBExt } from "./models"
export class SupabaseAPI {
constructor(private supabase: SupabaseClient<Database>) {}
async getExtList(): Promise<SBExt[]> {
const res = await this.supabase
.from("extensions")
.select(
"identifier, version, api_version, name, downloads, short_description, long_description, icon, created_at"
)
.order("downloads", { ascending: false })
.select()
const dbExts: Tables<"extensions">[] = res.data ?? []
return dbExts
.map((x) => {
const parsedNode = v.safeParse(SBExt, x)
if (!parsedNode.success) {
console.error(`Fail to parse extension`, x)
console.warn(parsedNode.issues)
console.error(v.flatten(parsedNode.issues))
}
return parsedNode.success ? v.parse(SBExt, parsedNode.output) : null
})
.filter((x) => x !== null)
}
async getLatestExtPublish(
identifier: string
): Promise<PostgrestSingleResponse<Tables<"ext_publish">>> {
return this.supabase
.from("ext_publish")
.select(
"created_at, name, version, manifest, shasum, size, tarball_path, cmd_count, identifier, downloads, demo_images, api_version, metadata"
)
.order("created_at", { ascending: false })
.eq("identifier", identifier)
.select()
.limit(1)
.single()
}
getExtension(identifier: string) {
return this.supabase
.from("extensions")
.select("*")
.eq("identifier", identifier)
.limit(1)
.single()
}
async incrementDownloads({
identifier,
version
}: {
identifier: string
version: string
}): Promise<{ downloads: number }> {
return this.supabase.functions
.invoke("increment-downloads", {
body: { identifier, version }
})
.then(({ data, error }) => {
if (error) {
throw error
}
const parsed = v.safeParse(
v.object({
downloads: v.number()
}),
data
)
if (!parsed.success) {
throw new Error("Fail to parse increment downloads response")
}
return parsed.output
})
}
async publishExtFromJSR(payload: {
scope: string
version: string
name: string
}): Promise<void> {
return this.supabase.functions
.invoke("publish-jsr-ext", {
body: payload
})
.then(async ({ data, error }) => {
if (data && data.isValid) {
return
}
if (error?.name === "FunctionsHttpError") {
const errorMessage = await error.context.json()
throw new Error(errorMessage.error)
} else {
throw new Error(`Unknown error: ${error?.message}`)
}
})
}
translateExtensionFilePathToUrl(tarballPath: string): string {
return this.supabase.storage.from("extensions").getPublicUrl(tarballPath).data.publicUrl
}
}