diff --git a/app/srv/api/code.ts b/app/srv/api/code.ts index 6c784b65..de6e512b 100644 --- a/app/srv/api/code.ts +++ b/app/srv/api/code.ts @@ -1,11 +1,84 @@ +import { dirAsync } from "fs-jetpack"; +import trim from "lodash/trim"; +import { dirname } from "path"; import { apiContext } from "service-srv"; +import { g } from "utils/global"; +import { baseTypings } from "../../web/src/utils/script/types/base"; export const _ = { url: "/code/:site_id/:action", - async api(site_id: string, action: "list") { + async api(site_id: string, action: "list" | "reload-api") { const { req, res } = apiContext(this); - if (action === "list") { + if (action === "reload-api") { + const site = await db.site.findFirst({ + select: { config: true }, + where: { id: site_id }, + }); + if (site && site.config) { + const base = trim((site.config as any).api_url || "", "/"); + const apires = await fetch(`${base}/_prasi/load.json`); + const json = (await apires.json()) as { + apiEntry: {}; + apiTypes: string; + prismaTypes: Record; + }; + let apiPath = ""; + if (typeof json.apiTypes === "string") { + apiPath = "gen/srv/api/srv"; + await Bun.write( + `${g.datadir}/site/code/${site_id}/api-types.d.ts`, + json.apiTypes + ); + } + + for (const [k, v] of Object.entries(json.prismaTypes)) { + await dirAsync(dirname(`${g.datadir}/site/code/${site_id}/${k}`)); + await Bun.write( + `${g.datadir}/site/code/${site_id}/${k}`, + JSON.parse(v) + ); + } + + await Bun.write( + `${g.datadir}/site/code/${site_id}/global.d.ts`, + `\ +import React from "react"; +import { + FC as ReactFC, + ReactNode as RNode, + ReactElement as RElement, +} from "react"; +import * as prisma from "./prisma"; +${iftext( + apiPath, + `\ +import "./api-types"; +import type * as SRVAPI from "${apiPath}"; + ` +)} + +declare global { + const db: prisma.PrismaClient; + ${baseTypings} + ${iftext( + apiPath, + `\ + type Api = typeof SRVAPI; + type ApiName = keyof Api; + const api: { [k in ApiName]: Awaited["_"]["api"] }; + ` + )} +} + +` + ); + + return new Response("OK"); + } + + return new Response("NOT FOUND", { status: 404 }); + } else if (action === "list") { let list = await db.code.findMany({ where: { id_site: site_id } }); if (!list.find((e) => e.name === "site")) { @@ -36,3 +109,10 @@ export const _ = { return "This is code.ts"; }, }; + +export const iftext = (condition: any, text: string) => { + if (condition) { + return text; + } + return ""; +}; diff --git a/app/web/src/render/editor/panel/script/monaco/typings.ts b/app/web/src/render/editor/panel/script/monaco/typings.ts index 44f7d505..4230bf91 100644 --- a/app/web/src/render/editor/panel/script/monaco/typings.ts +++ b/app/web/src/render/editor/panel/script/monaco/typings.ts @@ -91,7 +91,12 @@ declare module "ts:prisma" { register( monaco, `\ -import React from 'react'; +import React from "react"; +import { + FC as ReactFC, + ReactNode as RNode, + ReactElement as RElement, +} from "react"; import prisma from 'ts:prisma'; ${iftext( diff --git a/app/web/src/utils/script/types/base.ts b/app/web/src/utils/script/types/base.ts index 7bcdf9e3..9fd4aefa 100644 --- a/app/web/src/utils/script/types/base.ts +++ b/app/web/src/utils/script/types/base.ts @@ -1,98 +1,105 @@ export const baseTypings = ` -type FC = React.FC; -const Fragment = React.Fragment; -const ReactNode = React.ReactNode; -const useCallback = React.useCallback; -const useMemo = React.useMemo; -const ReactElement = React.ReactElement; -const isValidElement = React.isValidElement; -const useEffect = React.useEffect; -const useState = React.useState; + type FC = ReactFC; + const Fragment: typeof React.Fragment; + const ReactNode: RNode; + const useCallback: typeof React.useCallback; + const useMemo: typeof React.useMemo; + const ReactElement: RElement; + const isValidElement: typeof React.isValidElement; + const useEffect: typeof React.useEffect; + const useState: typeof React.useState; -const pathname: string; -const isEditor: boolean; -const isLayout: boolean; -const isMobile: boolean; -const isDesktop: boolean; -const preload: (pathname: string) => void; -const apiHeaders: Record; -const navigate: (url:string) => void; -const params: any; -const cx = (...classNames: any[]) => string; -const css = ( - tag: CSSAttribute | TemplateStringsArray | string, - ...props: Array -) => string; + const pathname: string; + const isEditor: boolean; + const isLayout: boolean; + const isMobile: boolean; + const isDesktop: boolean; + const preload: (pathname: string) => void; + const apiHeaders: Record; + const navigate: (url: string) => void; + const params: any; + const cx: (...classNames: any[]) => string; + const css: ( + tag: TemplateStringsArray | string, + ...props: Array + ) => string; -const props: { - className: string; - onPointerDown?: () => void; - onPointerMove?: () => void; - onPointerLeave?: () => void; -}; -const children: ReactNode; - -const PassProp: FC & {children: React.ReactNode; }>; -const PassChild: FC<{name: string}>; -const Preload: FC<{url: string[]}>; -const apiurl: string; -const pageid: string; -type ITEM = { - id: string - name: string; - type: 'item' | 'text'; - adv?: { - js?: string; - jsBuilt?: string; - css?: string; - html?: string; - }, - text: string, - html: string, - component?: { id:string, props: Record}, - childs: ITEM[] -} -const newElement: (gen?: (item: ITEM) => ITEM | ITEM[]) => React.ReactNode; -const mobile: { - notif: { - register: (user_id: string) => void; - send: (data: { - user_id: string, - title: string, - body: string, - data: any - }) => void; - onTap: (data: null |{ - user_id: string, - title: string, - body: string, - data: any - }) => void | Promise; - onReceive: (data: { - user_id: string, - title: string, - body: string, - data: any - }) => void | Promise - } -}; -const Local: >(arg: { - name: string; - idx?: any; - value: T; - children: ((local: T & { render: () => void }) => any); - deps?: any[]; - effect?: ( - local: T & { render: () => void } - ) => void | (() => void) | Promise void)>; - hook?: ( - local: T & { render: () => void } - ) => void | (() => void) | Promise void)>; - cache?: boolean; -}) => ReactNode; + const props: { + className: string; + onPointerDown?: () => void; + onPointerMove?: () => void; + onPointerLeave?: () => void; + }; + const children: RNode; + const PassProp: FC & { children: React.ReactNode }>; + const PassChild: FC<{ name: string }>; + const Preload: FC<{ url: string[] }>; + const apiurl: string; + const pageid: string; + type ITEM = { + id: string; + name: string; + type: "item" | "text"; + adv?: { + js?: string; + jsBuilt?: string; + css?: string; + html?: string; + }; + text: string; + html: string; + component?: { + id: string; + props: Record< + string, + { + value: string; + valueBuilt: string; + meta: { type: string }; + } + >; + }; + childs: ITEM[]; + }; + const newElement: (gen?: (item: ITEM) => ITEM | ITEM[]) => React.ReactNode; + const mobile: { + notif: { + register: (user_id: string) => void; + send: (data: { + user_id: string; + title: string; + body: string; + data: any; + }) => void; + onTap: ( + data: null | { + user_id: string; + title: string; + body: string; + data: any; + } + ) => void | Promise; + onReceive: (data: { + user_id: string; + title: string; + body: string; + data: any; + }) => void | Promise; + }; + }; + const Local: >(arg: { + name: string; + idx?: any; + value: T; + children: (local: T & { render: () => void }) => any; + deps?: any[]; + effect?: ( + local: T & { render: () => void } + ) => void | (() => void) | Promise void)>; + hook?: ( + local: T & { render: () => void } + ) => void | (() => void) | Promise void)>; + cache?: boolean; + }) => RNode `; diff --git a/app/web/src/utils/script/typings.ts b/app/web/src/utils/script/typings.ts index 1a44728c..bb6b23b1 100644 --- a/app/web/src/utils/script/typings.ts +++ b/app/web/src/utils/script/typings.ts @@ -94,7 +94,12 @@ declare module "ts:prisma" { register( monaco, `\ -import React from 'react'; +import React from "react"; +import { + FC as ReactFC, + ReactNode as RNode, + ReactElement as RElement, +} from "react"; import prisma from 'ts:prisma'; ${iftext( @@ -109,7 +114,6 @@ declare global {; ${baseTypings} - const moko: {nama: string}; ${propText.join("\n")} ${iftext(