diff --git a/app/web/src/base/load/api/api-proxy-def.tsx b/app/web/src/base/load/api/api-proxy-def.tsx new file mode 100644 index 00000000..1b06f148 --- /dev/null +++ b/app/web/src/base/load/api/api-proxy-def.tsx @@ -0,0 +1,62 @@ +import { w } from "../../../utils/types/general"; +import { fetchViaProxy } from "../proxy"; + +export const loadApiProxyDef = async (url: string, with_types: boolean) => { + const raw = await fetchViaProxy(urlPath(url, "/_prasi/_")); + let ver = ""; + if (raw && (raw as any).prasi) { + ver = (raw as any).prasi; + } + const base = baseUrl(url); + + if (ver === "v2") { + await new Promise((done) => { + const d = document; + const script = d.createElement("script"); + script.onload = async () => { + done(); + }; + if (with_types) { + script.src = `${base}/_prasi/load.js?url=${url}&dev=1`; + } else { + script.src = `${base}/_prasi/load.js?url=${url}`; + } + d.body.appendChild(script); + }); + } else { + const apiEntry = await fetch(base + "/_prasi/api-entry"); + w.prasiApi[url] = { + apiEntry: (await apiEntry.json()).srv, + }; + + if (with_types) { + const apiTypes = await fetch(base + "/_prasi/api-types"); + w.prasiApi[url].apiTypes = await apiTypes.text(); + w.prasiApi[url].prismaTypes = { + "prisma.d.ts": await loadText(`${base}/_prasi/prisma/index.d.ts`), + "runtime/index.d.ts": await loadText( + `${base}/_prasi/prisma/runtime/index.d.ts` + ), + "runtime/library.d.ts": await loadText( + `${base}/_prasi/prisma/runtime/library.d.ts` + ), + }; + } + } +}; + +const baseUrl = (url: string) => { + const base = new URL(url); + return `${base.protocol}//${base.host}`; +}; + +const urlPath = (url: string, pathname: string) => { + const base = new URL(url); + base.pathname = pathname; + return base.toString(); +}; + +const loadText = async (url: string, v2?: boolean) => { + const res = await fetch(url); + return await res.text(); +}; diff --git a/pkgs/web-utils/src/client-api.ts b/app/web/src/base/load/api/api-proxy.tsx similarity index 52% rename from pkgs/web-utils/src/client-api.ts rename to app/web/src/base/load/api/api-proxy.tsx index 46cf42f8..efb9eb62 100644 --- a/pkgs/web-utils/src/client-api.ts +++ b/app/web/src/base/load/api/api-proxy.tsx @@ -1,40 +1,49 @@ -import { fetchSendApi } from "./client-frame"; +import { w } from "../../../utils/types/general"; +import { fetchViaProxy } from "../proxy"; +import { loadApiProxyDef } from "./api-proxy-def"; -export const apiClient = ( - api: Record, - apiUrl: string -) => { +export type ApiProxy = {}> = any; + +export const apiProxy = (api_url: string) => { return new Proxy( {}, { get: (_, actionName: string) => { const createFn = (actionName: string) => { - return function (this: { apiUrl: string } | undefined, ...rest: any) { + return function ( + this: { api_url: string } | undefined, + ...rest: any + ) { return new Promise(async (resolve, reject) => { try { - let _apiURL = apiUrl; - if (typeof this?.apiUrl === "string") { - _apiURL = this.apiUrl; + let base_url = api_url; + if (typeof this?.api_url === "string") { + base_url = this.api_url; } - if (!api) { - reject( - new Error(`API Definition for ${_apiURL} is not loaded.`) - ); - return; + if (!w.prasiApi) { + w.prasiApi = {}; } - if (api && !api[actionName]) { - reject( - `API ${actionName.toString()} not found, existing API: \n - ${Object.keys( - api || {} - ).join("\n - ")}` - ); - return; + if (!w.prasiApi[base_url]) { + await loadApiProxyDef(base_url, false); } - let actionUrl = api[actionName].url; - const actionParams = api[actionName].args; + const api_def = w.prasiApi[base_url]; + if (api_def) { + if (!api_def.apiEntry) api_def.apiEntry = {}; + if (api_def.apiEntry && !api_def.apiEntry[actionName]) { + reject( + `API ${actionName.toString()} not found, existing API: \n - ${Object.keys( + api_def || {} + ).join("\n - ")}` + ); + return; + } + } + + let actionUrl = api_def.apiEntry[actionName].url; + const actionParams = api_def.apiEntry[actionName].args; if (actionUrl && actionParams) { if (rest.length > 0 && actionParams.length > 0) { for (const [idx, p] of Object.entries(rest)) { @@ -53,7 +62,7 @@ export const apiClient = ( } } - const url = `${_apiURL}${actionUrl}`; + const url = `${base_url}${actionUrl}`; const result = await fetchSendApi(url, rest); resolve(result); @@ -82,3 +91,9 @@ export const apiClient = ( } ); }; + +const fetchSendApi = async (url: string, params: any) => { + return await fetchViaProxy(url, params, { + "content-type": "application/json", + }); +}; diff --git a/pkgs/web-utils/src/client-db.ts b/app/web/src/base/load/db/db-proxy.tsx similarity index 70% rename from pkgs/web-utils/src/client-db.ts rename to app/web/src/base/load/db/db-proxy.tsx index 832511a7..0f1c667f 100644 --- a/pkgs/web-utils/src/client-db.ts +++ b/app/web/src/base/load/db/db-proxy.tsx @@ -1,8 +1,7 @@ -import { waitUntil } from "web-utils"; -import { createFrameCors } from "./client-frame"; import hash_sum from "hash-sum"; +import { fetchViaProxy } from "../proxy"; -export const dbClient = (name: string, dburl?: string) => { +export const dbProxy = (dburl: string) => { return new Proxy( {}, { @@ -10,7 +9,6 @@ export const dbClient = (name: string, dburl?: string) => { if (table === "_tables") { return () => { return fetchSendDb( - name, { name, action: "definition", @@ -24,7 +22,6 @@ export const dbClient = (name: string, dburl?: string) => { if (table === "_definition") { return (table: string) => { return fetchSendDb( - name, { name, action: "definition", @@ -38,7 +35,6 @@ export const dbClient = (name: string, dburl?: string) => { if (table.startsWith("$")) { return (...params: any[]) => { return fetchSendDb( - name, { name, action: "query", @@ -60,7 +56,6 @@ export const dbClient = (name: string, dburl?: string) => { action = "query"; } return fetchSendDb( - name, { name, action, @@ -83,37 +78,13 @@ const cachedQueryResult: Record< { timestamp: number; result: any; promise: Promise } > = {}; -export const fetchSendDb = async ( - name: string, - params: any, - dburl?: string -) => { - const w = typeof window === "object" ? window : (globalThis as any); - let url = `/_dbs/${name}`; - let frm: Awaited>; - +export const fetchSendDb = async (params: any, dburl: string) => { + const base = new URL(dburl); + base.pathname = `/_dbs/`; if (params.table) { - url += `/${params.table}`; - } - - const _base = dburl || w.serverurl; - - if (!w.frmapi) { - w.frmapi = {}; - } - - if (!w.frmapi[_base]) { - w.frmapi[_base] = await createFrameCors(_base); - } - - frm = w.frmapi[_base]; - - if (!frm) { - await waitUntil(() => { - frm = w.frmapi[_base]; - return frm; - }); + base.pathname += `/${params.table}`; } + const url = base.toString(); const hsum = hash_sum(params); const cached = cachedQueryResult[hsum]; @@ -121,7 +92,9 @@ export const fetchSendDb = async ( if (!cached || (cached && Date.now() - cached.timestamp > 1000)) { cachedQueryResult[hsum] = { timestamp: Date.now(), - promise: frm.send(url, params, w.apiHeaders), + promise: fetchViaProxy(url, params, { + "content-type": "application/json", + }), result: null, }; diff --git a/app/web/src/nova/vi/load/proxy.ts b/app/web/src/base/load/proxy.ts similarity index 78% rename from app/web/src/nova/vi/load/proxy.ts rename to app/web/src/base/load/proxy.ts index 31f12651..b25d5be9 100644 --- a/app/web/src/nova/vi/load/proxy.ts +++ b/app/web/src/base/load/proxy.ts @@ -47,11 +47,16 @@ export const fetchViaProxy = async ( const cur = new URL(location.href); const base = new URL(url); if (cur.host === base.host) { - const res = await fetch(base.pathname, { - method: "POST", - body, - headers, - }); + const res = await fetch( + base.pathname, + data + ? { + method: "POST", + body, + headers, + } + : undefined + ); const raw = await res.text(); try { return JSON.parse(raw); @@ -59,17 +64,15 @@ export const fetchViaProxy = async ( return raw; } } else { - console.log(url); - return null; - // const res = await fetch(`/_proxy`, { - // method: "POST", - // body: JSON.stringify({ - // url, - // body, - // headers, - // }), - // headers: { "content-type": "application/json" }, - // }); - // return res.json(); + const res = await fetch(`/_proxy`, { + method: "POST", + body: JSON.stringify({ + url, + body, + headers, + }), + headers: { "content-type": "application/json" }, + }); + return res.json(); } }; diff --git a/app/web/src/index.tsx b/app/web/src/index.tsx index a56b3703..a330ff61 100644 --- a/app/web/src/index.tsx +++ b/app/web/src/index.tsx @@ -1,12 +1,12 @@ import { Root as ReactRoot, createRoot } from "react-dom/client"; import { defineReact, defineWindow } from "web-utils"; +import { apiProxy } from "./base/load/api/api-proxy"; +import { dbProxy } from "./base/load/db/db-proxy"; import { Root } from "./base/root"; import "./index.css"; import { registerMobile } from "./render/live/logic/mobile"; -import { reloadDBAPI } from "./utils/script/init-api"; -import { w } from "./utils/types/general"; import { sworkerAddCache, sworkerRegister } from "./sworker-boot"; -import { dbClient } from "./base/load/db/client-db"; +import { w } from "./utils/types/general"; const start = async () => { const base = `${location.protocol}//${location.host}`; @@ -14,7 +14,12 @@ const start = async () => { root: null as null | ReactRoot, }; w.mobile = registerMobile(); - w.db = dbClient("prasi", location.origin); + + const cur = new URL(location.href); + const base_url = `${cur.protocol}//${cur.host}`; + w.db = dbProxy(base_url); + w.api = apiProxy(base_url); + w.serverurl = base; sworkerRegister(react); diff --git a/app/web/src/nova/ed/logic/ed-init.ts b/app/web/src/nova/ed/logic/ed-init.ts index 464e3aac..4371a0c6 100644 --- a/app/web/src/nova/ed/logic/ed-init.ts +++ b/app/web/src/nova/ed/logic/ed-init.ts @@ -1,17 +1,18 @@ import init from "wasm-gzip"; import { jscript } from "../../../utils/script/jscript"; -import { dbClient } from "../../../base/load/db/client-db"; +import { dbProxy } from "../../../base/load/db/db-proxy"; import { PG } from "./ed-global"; -import { fetchViaProxy } from "../../vi/load/proxy"; +import { fetchViaProxy } from "../../../base/load/proxy"; +import { ApiProxy, apiProxy } from "../../../base/load/api/api-proxy"; -let w = window as unknown as { db: ReturnType }; +let w = window as unknown as { + db: ReturnType; + api: ApiProxy; +}; export const edInit = async (p: PG) => { p.status = "ready"; - const cur = new URL(location.href); - w.db = dbClient("prasi", `${cur.protocol}//${cur.host}`); - await init(); jscript.init(p.render, { esbuild: false }); diff --git a/app/web/src/nova/ed/panel/main/main-per-item.tsx b/app/web/src/nova/ed/panel/main/main-per-item.tsx index 50b2cb04..813fd49f 100644 --- a/app/web/src/nova/ed/panel/main/main-per-item.tsx +++ b/app/web/src/nova/ed/panel/main/main-per-item.tsx @@ -1,6 +1,7 @@ import { IContent } from "../../../../utils/types/general"; import { VG } from "../../../vi/render/global"; import { IMeta, PG, active } from "../../logic/ed-global"; +import { treeRebuild } from "../../logic/tree/build"; type MPIVParam = Parameters>; export const mainPerItemVisit = ( @@ -59,6 +60,12 @@ export const mainPerItemVisit = ( parts.props.onPointerDown = (e) => { e.stopPropagation(); + if (active.comp_id && !p.comp.list[active.comp_id]) { + active.comp_id = ""; + treeRebuild(p); + return; + } + const item = getOuterItem( { meta: active.comp_id ? p.comp.list[active.comp_id].meta : p.page.meta, diff --git a/app/web/src/nova/ed/panel/popup/site/site-user.tsx b/app/web/src/nova/ed/panel/popup/site/site-user.tsx index e23b8bb4..4ba30d0e 100644 --- a/app/web/src/nova/ed/panel/popup/site/site-user.tsx +++ b/app/web/src/nova/ed/panel/popup/site/site-user.tsx @@ -119,7 +119,7 @@ export const EdPopUser = ({ ))} - {user.all.length > 0 && onAdd && ( + {Array.isArray(user.all) && user.all.length > 0 && onAdd && (