fix bun
This commit is contained in:
parent
02727d17fd
commit
e9b306e9a2
|
|
@ -0,0 +1,72 @@
|
|||
import { apiContext } from "service-srv";
|
||||
import argon from "@node-rs/argon2";
|
||||
import { session } from "utils/session";
|
||||
|
||||
export const _ = {
|
||||
url: "/_login",
|
||||
async api(username: string, password: string) {
|
||||
const { res, req } = apiContext(this);
|
||||
|
||||
const current = session.get(req);
|
||||
|
||||
if (!current) {
|
||||
const user = await db.user.findFirst({
|
||||
where: { OR: [{ username }, { phone: username }] },
|
||||
include: {
|
||||
org_user: {
|
||||
select: {
|
||||
org: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
org: {
|
||||
select: { id: true, name: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
if (user && user.org_user) {
|
||||
user.org = user.org_user.map((e) => e.org);
|
||||
delete (user as any).org_user;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!!user && (await argon.verify(user.password, password))) {
|
||||
//@ts-ignore
|
||||
delete user.password;
|
||||
const sdata = await session.new({ user });
|
||||
|
||||
let setDefaultCookie = true;
|
||||
const origin = req.headers.get("origin");
|
||||
if (origin) {
|
||||
const url = new URL(origin);
|
||||
if (url.hostname === "localhost") {
|
||||
setDefaultCookie = false;
|
||||
res.setHeader("set-cookie", `${session.cookieKey}=${sdata.id};`);
|
||||
}
|
||||
}
|
||||
|
||||
if (setDefaultCookie) {
|
||||
res.setHeader(
|
||||
"set-cookie",
|
||||
`${session.cookieKey}=${sdata.id}; SameSite=None; Secure; HttpOnly`
|
||||
);
|
||||
}
|
||||
return { status: "ok", session: sdata };
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e, user, password);
|
||||
}
|
||||
} else {
|
||||
return { status: "ok", session: current };
|
||||
}
|
||||
|
||||
return {
|
||||
status: "failed",
|
||||
reason: "Invalid username / password",
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
@ -1,9 +1,37 @@
|
|||
import { apiContext } from "service-srv";
|
||||
import { session } from "utils/session";
|
||||
import { user } from "dbgen";
|
||||
|
||||
export const _ = {
|
||||
url: "/session)}",
|
||||
url: "/session",
|
||||
async api() {
|
||||
const { req, res } = apiContext(this);
|
||||
return "This is session.ts";
|
||||
const sdata = session.get<{
|
||||
user: user & {
|
||||
org: {
|
||||
id: string;
|
||||
name: string;
|
||||
}[];
|
||||
};
|
||||
}>(req);
|
||||
if (sdata) {
|
||||
let setDefaultCookie = true;
|
||||
const origin = req.headers.get("origin");
|
||||
if (origin) {
|
||||
const url = new URL(origin);
|
||||
if (url.hostname === "localhost") {
|
||||
setDefaultCookie = false;
|
||||
res.setHeader("set-cookie", `${session.cookieKey}=${sdata.id};`);
|
||||
}
|
||||
}
|
||||
|
||||
if (setDefaultCookie) {
|
||||
res.setHeader(
|
||||
"set-cookie",
|
||||
`${session.cookieKey}=${sdata.id}; SameSite=None; Secure; HttpOnly`
|
||||
);
|
||||
}
|
||||
}
|
||||
return sdata;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,31 @@
|
|||
declare module "api/auth/login" {
|
||||
export const _: {
|
||||
url: string;
|
||||
api(username: string, password: string): Promise<{
|
||||
status: string;
|
||||
session: any;
|
||||
reason?: undefined;
|
||||
} | {
|
||||
status: string;
|
||||
reason: string;
|
||||
session?: undefined;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
declare module "api/session" {
|
||||
export const _: {
|
||||
url: string;
|
||||
api(): Promise<string>;
|
||||
api(): Promise<any>;
|
||||
};
|
||||
}
|
||||
declare module "exports" {
|
||||
export const login: {
|
||||
name: string;
|
||||
url: string;
|
||||
path: string;
|
||||
args: string[];
|
||||
handler: Promise<typeof import("api/auth/login")>;
|
||||
};
|
||||
export const session: {
|
||||
name: string;
|
||||
url: string;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
export const login = {
|
||||
name: "login",
|
||||
url: "/_login",
|
||||
path: "app/srv/api/auth/login.ts",
|
||||
args: ["username","password"],
|
||||
handler: import("./api/auth/login")
|
||||
}
|
||||
export const session = {
|
||||
name: "session",
|
||||
url: "/session)}",
|
||||
url: "/session",
|
||||
path: "app/srv/api/session.ts",
|
||||
args: [],
|
||||
handler: import("./api/session")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { FC } from "react";
|
||||
import { useLocal } from "web-utils";
|
||||
import { FieldColor } from "../ui/FieldColor";
|
||||
import { FieldImg } from "../ui/FieldImg";
|
||||
import { dropdownProp } from "../ui/style";
|
||||
import { Button } from "../ui/Button";
|
||||
import { FNBackground } from "../../../../../utils/types/meta-fn";
|
||||
|
|
@ -53,81 +52,6 @@ export const PanelBackground: FC<{
|
|||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip asChild content={"Background Image"}>
|
||||
<div
|
||||
className={cx(
|
||||
"bg-white p-[2px] border flex flex-1 border-gray-300",
|
||||
css`
|
||||
> * {
|
||||
flex: 1;
|
||||
}
|
||||
`
|
||||
)}
|
||||
>
|
||||
<FieldImg
|
||||
value={bg.url}
|
||||
update={async (url) => {
|
||||
update("bg", { ...bg, url });
|
||||
|
||||
if (
|
||||
value.type === "item" &&
|
||||
(!value.dim ||
|
||||
(value.dim && (!value.dim.w || value.dim.w === "fit")))
|
||||
) {
|
||||
const img = await getImgMeta(`${siteApiUrl}${url}`);
|
||||
(update as any)("dim", {
|
||||
w: Math.min(500, img?.width || 100),
|
||||
h: Math.min(500, img?.height || 100),
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
{bg.url ? (
|
||||
<>
|
||||
<Tooltip asChild content={"Unlink Image"}>
|
||||
<div className={"flex flex-row bg-white"}>
|
||||
<Button
|
||||
className={cx(
|
||||
"flex-1 flex-grow",
|
||||
css`
|
||||
height: 30px;
|
||||
width: 20px;
|
||||
max-width: 20px;
|
||||
padding: 0px !important;
|
||||
min-width: 0px !important;
|
||||
`
|
||||
)}
|
||||
onClick={(e) => {
|
||||
update("bg", { ...bg, url: "" });
|
||||
}}
|
||||
>
|
||||
<div className="w-[10px] flex items-center justify-center">
|
||||
<>
|
||||
<div className="text-lg text-gray-700">
|
||||
<svg
|
||||
width="15"
|
||||
height="15"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M16.949 14.121L19.071 12a5.008 5.008 0 0 0 0-7.071a5.006 5.006 0 0 0-7.071 0l-.707.707l1.414 1.414l.707-.707a3.007 3.007 0 0 1 4.243 0a3.005 3.005 0 0 1 0 4.243l-2.122 2.121a2.723 2.723 0 0 1-.844.57L13.414 12l1.414-1.414l-.707-.707a4.965 4.965 0 0 0-3.535-1.465c-.235 0-.464.032-.691.066L3.707 2.293L2.293 3.707l18 18l1.414-1.414l-5.536-5.536c.277-.184.538-.396.778-.636zm-6.363 3.536a3.007 3.007 0 0 1-4.243 0a3.005 3.005 0 0 1 0-4.243l1.476-1.475l-1.414-1.414L4.929 12a5.008 5.008 0 0 0 0 7.071a4.983 4.983 0 0 0 3.535 1.462A4.982 4.982 0 0 0 12 19.071l.707-.707l-1.414-1.414l-.707.707z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-stretch space-x-2">
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
import { FC, useEffect } from "react";
|
||||
import { useLocal } from "web-utils";
|
||||
import { FilePicker } from "./FilePicker";
|
||||
import { Button } from "./Button";
|
||||
import { FileImageGallery } from "./FileImageGallery";
|
||||
|
||||
export const FieldImg: FC<{
|
||||
value?: string;
|
||||
update: (value: string) => void;
|
||||
}> = ({ value, update }) => {
|
||||
const local = useLocal({ val: "", open: false });
|
||||
|
||||
useEffect(() => {
|
||||
local.val = value || "";
|
||||
local.render();
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
className="btn-hover h-[22px]"
|
||||
appearance="subtle"
|
||||
onClick={() => {
|
||||
local.open = true;
|
||||
local.render();
|
||||
}}
|
||||
>
|
||||
Image
|
||||
</Button>
|
||||
{local.open && (
|
||||
<FileImageGallery
|
||||
value={value}
|
||||
update={update}
|
||||
meta={local}
|
||||
onClose={() => {
|
||||
local.open = false;
|
||||
local.render();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,421 +0,0 @@
|
|||
import { FC } from "react";
|
||||
import { fetchSendApi } from "web-utils/src/web/iframe-cors";
|
||||
import { useGlobal, useLocal } from "web-utils";
|
||||
import { Gallery } from "./Gallery";
|
||||
import { Loading } from "../../../../../utils/ui/loading";
|
||||
import { ToolbarBox } from "../../../../../utils/ui/box";
|
||||
import { EditorGlobal } from "../../../logic/global";
|
||||
export const FileImageGallery: FC<{
|
||||
value?: string;
|
||||
update: (src: string) => void;
|
||||
onClose: () => void;
|
||||
accept?: string;
|
||||
type?: string;
|
||||
meta?: any;
|
||||
}> = ({
|
||||
meta,
|
||||
onClose,
|
||||
update,
|
||||
value,
|
||||
accept = "video/mp4, image/jpeg, image/png, image/jpg, image/x-icon, image/vnd.microsoft.icon",
|
||||
type = "image",
|
||||
}) => {
|
||||
const p = useGlobal(EditorGlobal, "EDITOR");
|
||||
const local = useLocal(
|
||||
{
|
||||
value: value || "",
|
||||
load: true,
|
||||
mode: "upload",
|
||||
preview: false as boolean,
|
||||
previewUrl: "" as string,
|
||||
isUpload: false as boolean,
|
||||
selectUrl: "" as string,
|
||||
},
|
||||
async () => {
|
||||
local.mode = "gallery";
|
||||
if (local.previewUrl) {
|
||||
local.selectUrl = local.previewUrl;
|
||||
}
|
||||
local.render();
|
||||
}
|
||||
);
|
||||
const onUpload: React.ChangeEventHandler<HTMLInputElement> = async function (
|
||||
e
|
||||
) {
|
||||
local.isUpload = true;
|
||||
local.render();
|
||||
const files = e.currentTarget.files;
|
||||
if (files && p.page) {
|
||||
const res: string[] = await fetchSendApi(
|
||||
`${siteApiUrl}/_upload/${p.page.id}`,
|
||||
files[0]
|
||||
);
|
||||
local.previewUrl = res[0];
|
||||
local.preview = true;
|
||||
local.selectUrl = local.previewUrl;
|
||||
local.render();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="fixed inset-0 bg-black bg-opacity-10 cursor-pointer"
|
||||
onClick={() => {
|
||||
meta.open = false;
|
||||
meta.render();
|
||||
}}
|
||||
></div>
|
||||
<div className="fixed inset-[50px] bg-white shadow-2xl z-50 ">
|
||||
{false ? (
|
||||
<div className="flex w-full h-full items-center justify-center">
|
||||
<Loading note="img-gallery" backdrop={false} />
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={cx(
|
||||
"relative w-full h-full flex flex-col items-stretch",
|
||||
css`
|
||||
contain: content;
|
||||
overflow: auto;
|
||||
|
||||
> ul {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.dropping {
|
||||
background: #efefff;
|
||||
}
|
||||
`
|
||||
)}
|
||||
>
|
||||
<div className="w-full flex flex-row">
|
||||
<div
|
||||
className={cx(
|
||||
"toolbar",
|
||||
"flex flex-row flex-grow px-2 justify-center items-center"
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cx(
|
||||
"toolbar-right",
|
||||
"flex mr-2 ",
|
||||
css`
|
||||
align-items: center !important;
|
||||
border-bottom: 0px !important;
|
||||
height: auto !important;
|
||||
|
||||
.toolbar-box {
|
||||
height: 22px;
|
||||
// margin: 0px !important;
|
||||
}
|
||||
`
|
||||
)}
|
||||
>
|
||||
<ToolbarBox
|
||||
items={[
|
||||
{
|
||||
onClick() {
|
||||
local.mode = "upload";
|
||||
local.render();
|
||||
},
|
||||
className: cx(
|
||||
local.mode === "upload" &&
|
||||
"border-b-2 border-blue-500 bg-blue-50"
|
||||
),
|
||||
content: (
|
||||
<>
|
||||
<div className="flex flex-row items-center justify-center space-x-2">
|
||||
<div className="text-lg text-gray-700">
|
||||
<svg
|
||||
width="15"
|
||||
height="15"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m19.21 12.04l-1.53-.11l-.3-1.5A5.484 5.484 0 0 0 12 6C9.94 6 8.08 7.14 7.12 8.96l-.5.95l-1.07.11A3.99 3.99 0 0 0 2 14c0 2.21 1.79 4 4 4h13c1.65 0 3-1.35 3-3c0-1.55-1.22-2.86-2.79-2.96zm-5.76.96v3h-2.91v-3H8l4-4l4 4h-2.55z"
|
||||
opacity=".3"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M19.35 10.04A7.49 7.49 0 0 0 12 4C9.11 4 6.6 5.64 5.35 8.04A5.994 5.994 0 0 0 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5c0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4c0-2.05 1.53-3.76 3.56-3.97l1.07-.11l.5-.95A5.469 5.469 0 0 1 12 6c2.62 0 4.88 1.86 5.39 4.43l.3 1.5l1.53.11A2.98 2.98 0 0 1 22 15c0 1.65-1.35 3-3 3zM8 13h2.55v3h2.9v-3H16l-4-4z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span>Upload File</span>
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
onClick() {
|
||||
local.mode = "gallery";
|
||||
local.render();
|
||||
},
|
||||
className: cx(
|
||||
local.mode === "gallery" &&
|
||||
"border-b-2 border-blue-500 bg-blue-50"
|
||||
),
|
||||
content: (
|
||||
<>
|
||||
<div className="flex flex-row items-center justify-center space-x-2">
|
||||
<div className="text-lg text-gray-700">
|
||||
<svg
|
||||
width="15"
|
||||
height="15"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g fill="currentColor">
|
||||
<path d="M18.512 10.077c0 .738-.625 1.337-1.396 1.337c-.77 0-1.395-.599-1.395-1.337c0-.739.625-1.338 1.395-1.338s1.396.599 1.396 1.338Z" />
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M18.036 5.532c-1.06-.137-2.414-.137-4.123-.136h-3.826c-1.71 0-3.064 0-4.123.136c-1.09.14-1.974.437-2.67 1.104S2.29 8.149 2.142 9.195C2 10.21 2 11.508 2 13.147v.1c0 1.64 0 2.937.142 3.953c.147 1.046.456 1.892 1.152 2.559c.696.667 1.58.963 2.67 1.104c1.06.136 2.414.136 4.123.136h3.826c1.71 0 3.064 0 4.123-.136c1.09-.14 1.974-.437 2.67-1.104s1.005-1.514 1.152-2.559C22 16.184 22 14.886 22 13.248v-.1c0-1.64 0-2.937-.142-3.953c-.147-1.046-.456-1.892-1.152-2.559c-.696-.667-1.58-.963-2.67-1.104ZM6.15 6.858c-.936.12-1.475.346-1.87.724c-.393.377-.629.894-.755 1.791c-.1.72-.123 1.619-.128 2.795l.47-.395c1.125-.942 2.819-.888 3.875.124l3.99 3.825a1.2 1.2 0 0 0 1.491.124l.278-.187a3.606 3.606 0 0 1 4.34.25l2.407 2.077c.098-.264.173-.579.227-.964c.128-.916.13-2.124.13-3.824c0-1.7-.002-2.909-.13-3.825c-.126-.897-.362-1.414-.756-1.791c-.393-.378-.933-.604-1.869-.724c-.956-.124-2.216-.125-3.99-.125h-3.72c-1.774 0-3.034.001-3.99.125Z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
<path
|
||||
d="M17.087 2.61c-.86-.11-1.955-.11-3.32-.11h-3.09c-1.364 0-2.459 0-3.318.11c-.89.115-1.633.358-2.222.92a2.9 2.9 0 0 0-.724 1.12c.504-.23 1.074-.366 1.714-.45c1.085-.14 2.47-.14 4.22-.14h3.915c1.749 0 3.134 0 4.219.14c.559.073 1.064.186 1.52.366a2.875 2.875 0 0 0-.693-1.035c-.589-.563-1.331-.806-2.221-.92Z"
|
||||
opacity=".5"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<span>Gallery</span>
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* <ResponsiveToggle /> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-grow flex flex-col">
|
||||
{local.mode === "gallery" ? (
|
||||
<>
|
||||
<Gallery value={value} update={update} meta={local} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex flex-row items-center relative px-2 py-4 space-x-4 justify-center flex-1">
|
||||
<div className="relative flex flex-row p-2 cursor-pointer bg-blue-500 text-white">
|
||||
<span>Upload Image</span>
|
||||
<input
|
||||
type="file"
|
||||
name="file"
|
||||
className={cx(
|
||||
"absolute inset-0 opacity-0 cursor-pointer w-full h-full"
|
||||
)}
|
||||
accept={accept}
|
||||
onChange={onUpload}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row flex-grow items-center justify-center px-2 py-4 space-x-4">
|
||||
{local.preview ? (
|
||||
<>
|
||||
<div
|
||||
className={cx(
|
||||
"border-2 rounded mx-2 flex flex-row flex-grow h-full items-center justify-center px-2 py-4 space-x-4",
|
||||
css`
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
#d1d1d1 25%,
|
||||
transparent 25%
|
||||
),
|
||||
linear-gradient(
|
||||
-45deg,
|
||||
#d1d1d1 25%,
|
||||
transparent 25%
|
||||
),
|
||||
linear-gradient(
|
||||
45deg,
|
||||
transparent 75%,
|
||||
#d1d1d1 75%
|
||||
),
|
||||
linear-gradient(
|
||||
-45deg,
|
||||
transparent 75%,
|
||||
#d1d1d1 75%
|
||||
);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 0 10px, 10px -10px,
|
||||
-10px 0px;
|
||||
`
|
||||
)}
|
||||
>
|
||||
<a
|
||||
href={`${siteApiUrl}${local.previewUrl}`}
|
||||
className={cx(
|
||||
"bg-no-repeat bg-contain bg-center rounded mx-2 flex flex-row flex-grow h-full items-center justify-center px-2 py-4 space-x-4",
|
||||
css`
|
||||
background-image: url("${siteApiUrl}${local.previewUrl}");
|
||||
`
|
||||
)}
|
||||
></a>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{local.isUpload ? (
|
||||
<>
|
||||
<div
|
||||
className={cx(
|
||||
"border-2 rounded mx-2 flex flex-row flex-grow h-full items-center justify-center px-2 py-4 space-x-4"
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col space-y-2">
|
||||
<div className="text-lg text-gray-700">
|
||||
<svg
|
||||
width="150"
|
||||
height="150"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<mask id="lineMdCloudUploadOutlineLoop0">
|
||||
<g fill="#fff">
|
||||
<circle cx="12" cy="10" r="6" />
|
||||
<rect
|
||||
width="9"
|
||||
height="8"
|
||||
x="8"
|
||||
y="12"
|
||||
/>
|
||||
<rect
|
||||
width="17"
|
||||
height="12"
|
||||
x="1"
|
||||
y="8"
|
||||
rx="6"
|
||||
>
|
||||
<animate
|
||||
attributeName="x"
|
||||
dur="24s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0;1;2;1"
|
||||
/>
|
||||
</rect>
|
||||
<rect
|
||||
width="17"
|
||||
height="10"
|
||||
x="6"
|
||||
y="10"
|
||||
rx="5"
|
||||
>
|
||||
<animate
|
||||
attributeName="x"
|
||||
dur="15s"
|
||||
repeatCount="indefinite"
|
||||
values="6;5;6;7;6"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<circle cx="12" cy="10" r="4" />
|
||||
<rect width="8" height="8" x="8" y="10" />
|
||||
<rect
|
||||
width="11"
|
||||
height="8"
|
||||
x="3"
|
||||
y="10"
|
||||
rx="4"
|
||||
>
|
||||
<animate
|
||||
attributeName="x"
|
||||
dur="24s"
|
||||
repeatCount="indefinite"
|
||||
values="3;2;3;4;3"
|
||||
/>
|
||||
</rect>
|
||||
<rect
|
||||
width="13"
|
||||
height="6"
|
||||
x="8"
|
||||
y="12"
|
||||
rx="3"
|
||||
>
|
||||
<animate
|
||||
attributeName="x"
|
||||
dur="15s"
|
||||
repeatCount="indefinite"
|
||||
values="8;7;8;9;8"
|
||||
/>
|
||||
</rect>
|
||||
<g fill="#fff">
|
||||
<rect
|
||||
width="3"
|
||||
height="4"
|
||||
x="10.5"
|
||||
y="12"
|
||||
/>
|
||||
<path d="M12 9L16 13H8L12 9Z">
|
||||
<animateMotion
|
||||
calcMode="linear"
|
||||
dur="1.5s"
|
||||
keyPoints="0;0.25;0.5;0.75;1"
|
||||
keyTimes="0;0.1;0.5;0.8;1"
|
||||
path="M0 0v-1v2z"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</path>
|
||||
</g>
|
||||
</mask>
|
||||
<rect
|
||||
width="24"
|
||||
height="24"
|
||||
fill="currentColor"
|
||||
mask="url(#lineMdCloudUploadOutlineLoop0)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex flex-row space-x-2 items-center justify-center text-xl">
|
||||
<span className="flex flex-row space-x-2">
|
||||
Uploading...
|
||||
</span>
|
||||
{/* <span className="flex flex-row space-x-2 font-medium">
|
||||
10%
|
||||
</span> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{local.selectUrl && (
|
||||
<div className="border-t-2 flex flex-row items-center justify-end p-4">
|
||||
<div
|
||||
onClick={() => {
|
||||
// FieldImg;
|
||||
update(local.selectUrl);
|
||||
meta.open = false;
|
||||
meta.render();
|
||||
}}
|
||||
className="relative flex flex-row p-2 cursor-pointer bg-blue-500 text-white font-medium px-6"
|
||||
>
|
||||
<span>Choose Image</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
import { FC, useTransition } from "react";
|
||||
import { fetchSendApi } from "web-utils/src/web/iframe-cors";
|
||||
import { useLocal } from "web-utils";
|
||||
import { Button } from "./Button";
|
||||
export const FilePicker: FC<{
|
||||
value?: string;
|
||||
update: (src: string) => void;
|
||||
onClose: () => void;
|
||||
accept?: string;
|
||||
type?: string;
|
||||
}> = ({
|
||||
onClose,
|
||||
update,
|
||||
value,
|
||||
accept = "video/mp4, image/jpeg, image/png, image/jpg, image/x-icon, image/vnd.microsoft.icon",
|
||||
type = "image",
|
||||
}) => {
|
||||
const local = useLocal({
|
||||
value: value || "",
|
||||
load: true,
|
||||
});
|
||||
|
||||
const [_, tx] = useTransition();
|
||||
const onUpload: React.ChangeEventHandler<HTMLInputElement> = async function (
|
||||
e
|
||||
) {
|
||||
const files = e.currentTarget.files;
|
||||
if (files) {
|
||||
// const res: string[] = (await fetchSendApi(
|
||||
// `${siteApiUrl}/_upload${site ? `/${site.id}` : ""}`,
|
||||
// files[0]
|
||||
// )) as any;
|
||||
// if (res) {
|
||||
// const val = res[0];
|
||||
// local.value = val;
|
||||
// local.render();
|
||||
// update(val);
|
||||
// }
|
||||
// onClose();
|
||||
}
|
||||
};
|
||||
|
||||
return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col items-stretch space-y-2 flex-1">
|
||||
<div className="flex-1 flex flex-col relative">
|
||||
<div className="absolute inset-0 flex overflow-hidden pointer-events-none items-center text-center justify-center">
|
||||
{local.load && local.value && (
|
||||
<div className={cx("self-center")}>Loading</div>
|
||||
)}
|
||||
{!!local.value ? (
|
||||
<>
|
||||
{type === "image" && (
|
||||
<img
|
||||
src={
|
||||
local.value.startsWith("http")
|
||||
? local.value
|
||||
: `${siteApiUrl}${local.value}?w=500&h=500&fit=contain`
|
||||
}
|
||||
className={cx(
|
||||
css`
|
||||
visibility: "visible";
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
`,
|
||||
"self-center"
|
||||
)}
|
||||
onLoad={() => {
|
||||
local.load = false;
|
||||
local.render();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{type === "pdf" && (
|
||||
<div className=" text-9xl text-green-600 pb-8">
|
||||
<PdfDocument />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div className="flex flex-col items-center">
|
||||
<span>Please Upload Image</span>
|
||||
<i className="italic"> — or —</i>
|
||||
<span>Fill Image URL</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
name="file"
|
||||
className={cx("absolute inset-0 opacity-0")}
|
||||
accept={accept}
|
||||
onChange={onUpload}
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
value={local.value}
|
||||
spellCheck={false}
|
||||
placeholder="Image URL"
|
||||
onChange={(e) => {
|
||||
local.value = e.currentTarget.value;
|
||||
local.render();
|
||||
|
||||
tx(() => {
|
||||
update(local.value);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<div className="flex justify-between">
|
||||
<input type="file" name="file" accept={accept} onChange={onUpload} />
|
||||
<Button
|
||||
onClick={() => {
|
||||
local.value = "";
|
||||
local.render();
|
||||
|
||||
tx(() => {
|
||||
update(local.value);
|
||||
});
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const PdfDocument = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="11"
|
||||
height="11"
|
||||
fill="none"
|
||||
viewBox="0 0 100 125"
|
||||
>
|
||||
{" "}
|
||||
<path
|
||||
fill="#000"
|
||||
fillRule="evenodd"
|
||||
d="M58.993 32A2.992 2.992 0 0156 29.007V4H19.666A3.665 3.665 0 0016 7.668v82.664A3.674 3.674 0 0019.68 94h60.64A3.683 3.683 0 0084 90.322V32H58.993zm6.919 35.403c-.544.274-1.36.549-2.448.549-2.176 0-5.44-.549-8.16-1.92-4.624.549-8.16 1.097-10.88 2.194-.272 0-.272 0-.544.274-3.264 5.758-5.984 8.5-8.16 8.5-.544 0-.816 0-1.088-.274l-1.36-.823v-.274C33 75.081 33 74.806 33 74.259c.272-1.372 1.904-3.84 5.168-5.759.544-.274 1.36-.823 2.448-1.371.816-1.37 1.632-3.016 2.72-4.935 1.36-2.742 2.176-5.484 2.992-7.952-1.088-3.29-1.632-5.21-.544-9.048C46.056 44.097 46.872 43 47.96 43h.544c.544 0 1.088.274 1.632.548 1.904 1.92 1.088 6.307 0 9.871v.275c1.088 3.016 2.72 5.483 4.352 7.129.816.548 1.36 1.096 2.448 1.645 1.36 0 2.448-.274 3.536-.274 3.264 0 5.44.548 6.256 1.919.272.548.272 1.097.272 1.645-.272.274-.544 1.097-1.088 1.645zM48.232 56.71c-.544 1.919-1.632 4.113-2.72 6.58-.544 1.097-1.088 1.92-1.632 3.016h.544c3.536-1.37 6.8-2.193 8.976-2.467-.544-.274-.816-.549-1.088-.823-1.36-1.645-2.992-3.839-4.08-6.306zm16.592 8.5c-.272-.275-1.36-1.097-5.168-1.097h-.544v.274c1.904.823 3.808 1.371 5.168 1.371H65.096v-.274s-.272 0-.272-.274zM39.8 69.323c-.544.274-1.088.548-1.36.822-1.904 1.645-3.264 3.565-3.536 4.387 1.632-.274 3.264-1.92 4.896-5.21zm8.16-18.92c.272-1.097.544-1.645.544-2.468v-.548c.272-1.37.272-2.468 0-2.742v-.274l-.272-.274s0 .274-.272.274c-.544 1.645-.544 3.564 0 6.032zM84 28H62.005A2.005 2.005 0 0160 25.995V4l24 24z"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
|
|
@ -1,268 +0,0 @@
|
|||
import { format } from "date-fns";
|
||||
import get from "lodash.get";
|
||||
import { FC, useTransition } from "react";
|
||||
import { fetchSendApi } from "web-utils/src/web/iframe-cors";
|
||||
import { useGlobal, useLocal } from "web-utils";
|
||||
import { Loading } from "../../../../../utils/ui/loading";
|
||||
import { EditorGlobal } from "../../../logic/global";
|
||||
export const Gallery: FC<{
|
||||
value?: string;
|
||||
update: (src: string) => void;
|
||||
onClose?: () => void;
|
||||
accept?: string;
|
||||
type?: string;
|
||||
meta?: any;
|
||||
}> = ({
|
||||
meta,
|
||||
onClose,
|
||||
update,
|
||||
value,
|
||||
accept = "video/mp4, image/jpeg, image/png, image/jpg, image/x-icon, image/vnd.microsoft.icon",
|
||||
type = "image",
|
||||
}) => {
|
||||
const p = useGlobal(EditorGlobal, "EDITOR");
|
||||
const local = useLocal(
|
||||
{
|
||||
value: value || "",
|
||||
load: true,
|
||||
mode: "upload",
|
||||
list: [] as Array<any>,
|
||||
ready: false,
|
||||
isPreview: false,
|
||||
hover: null as any,
|
||||
preview: {
|
||||
url: "" as string,
|
||||
dimension: {
|
||||
width: 0 as number,
|
||||
height: 0 as number,
|
||||
},
|
||||
details: null as any,
|
||||
},
|
||||
},
|
||||
async () => {
|
||||
local.isPreview = false;
|
||||
local.ready = false;
|
||||
local.render();
|
||||
if (p.page) {
|
||||
let res = (await fetchSendApi(
|
||||
`${siteApiUrl}/get-gallery/${p.page.id}`,
|
||||
{}
|
||||
)) as any;
|
||||
local.list = res.data;
|
||||
}
|
||||
local.ready = true;
|
||||
local.render();
|
||||
}
|
||||
);
|
||||
|
||||
const [_, tx] = useTransition();
|
||||
const getSize = (size: number) => {
|
||||
let hz = "";
|
||||
if (size < 1024) hz = size + " B";
|
||||
else if (size < 1024 * 1024) hz = (size / 1024).toFixed(2) + " KB";
|
||||
else if (size < 1024 * 1024 * 1024)
|
||||
hz = (size / 1024 / 1024).toFixed(2) + " MB";
|
||||
else hz = (size / 1024 / 1024 / 1024).toFixed(2) + " GB";
|
||||
return hz;
|
||||
};
|
||||
const loadImage: any = (imageSrc: any) =>
|
||||
new Promise((resolve) => {
|
||||
const image = new Image();
|
||||
image.onload = () => {
|
||||
const height = image.height;
|
||||
const width = image.width;
|
||||
resolve({ image, width, height });
|
||||
};
|
||||
image.src = imageSrc;
|
||||
});
|
||||
const onUpload: React.ChangeEventHandler<HTMLInputElement> = async function (
|
||||
e
|
||||
) {
|
||||
const files = e.currentTarget.files;
|
||||
if (files) {
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{local.ready ? (
|
||||
<>
|
||||
<div className="flex flex-row flex-grow w-full">
|
||||
<div className="flex flex-row flex-grow">
|
||||
<div className="flex-row flex p-2 relative flex-grow overflow-auto">
|
||||
<div
|
||||
className={cx(
|
||||
"flex items-start flex-wrap pl-[10px] absolute w-full h-full top-0 left-0"
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
{local.list.length ? (
|
||||
<>
|
||||
{local.list.map((e, idx) => {
|
||||
const bgurl = `${siteApiUrl}${get(e, "url")}`;
|
||||
return (
|
||||
<div
|
||||
key={e.url}
|
||||
className={cx(
|
||||
"relative flex flex-col items-start w-[200px] h-[100px] p-2 text-sm cursor-pointer hover:bg-blue-100 ml-1 mb-1",
|
||||
"justify-between transition-all",
|
||||
"hover:border-blue-500",
|
||||
"bg-no-repeat bg-cover bg-center",
|
||||
local.preview.url ===
|
||||
`${siteApiUrl}${get(e, "url")}`
|
||||
? "border-4 border-blue-500 shadow-xl"
|
||||
: "border",
|
||||
css`
|
||||
background-image: url("${bgurl}");
|
||||
.edit {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.edit {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
`
|
||||
)}
|
||||
onClick={async () => {
|
||||
const { width, height } = await loadImage(
|
||||
`${siteApiUrl}${get(e, "url")}`
|
||||
);
|
||||
local.preview = {
|
||||
url: `${siteApiUrl}${get(e, "url")}`,
|
||||
dimension: {
|
||||
width,
|
||||
height,
|
||||
},
|
||||
details: e,
|
||||
};
|
||||
local.isPreview = true;
|
||||
meta.selectUrl = `${siteApiUrl}${get(e, "url")}`;
|
||||
meta.render();
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
local.hover = `${siteApiUrl}${get(e, "url")}`;
|
||||
local.render();
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
local.hover = null;
|
||||
local.render();
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row flex-grow items-end justify-end">
|
||||
{local.hover ===
|
||||
`${siteApiUrl}${get(e, "url")}` ? (
|
||||
<>
|
||||
{" "}
|
||||
<div
|
||||
onClick={async (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
await fetchSendApi(
|
||||
`${siteApiUrl}/_delete`,
|
||||
{
|
||||
path: get(e, "path"),
|
||||
}
|
||||
);
|
||||
let list = local.list.filter(
|
||||
(x) => get(x, "path") !== get(e, "path")
|
||||
);
|
||||
local.list = list;
|
||||
local.render();
|
||||
}}
|
||||
className="relative flex flex-row p-2 cursor-pointer bg-red-500 rounded font-medium"
|
||||
>
|
||||
<div className="text-lg text-gray-700">
|
||||
<svg
|
||||
width="15"
|
||||
height="15"
|
||||
viewBox="0 0 28 28"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M11.5 6h5a2.5 2.5 0 0 0-5 0ZM10 6a4 4 0 0 1 8 0h6.25a.75.75 0 0 1 0 1.5h-1.31l-1.217 14.603A4.25 4.25 0 0 1 17.488 26h-6.976a4.25 4.25 0 0 1-4.235-3.897L5.06 7.5H3.75a.75.75 0 0 1 0-1.5H10ZM7.772 21.978a2.75 2.75 0 0 0 2.74 2.522h6.976a2.75 2.75 0 0 0 2.74-2.522L21.436 7.5H6.565l1.207 14.478ZM11.75 11a.75.75 0 0 1 .75.75v8.5a.75.75 0 0 1-1.5 0v-8.5a.75.75 0 0 1 .75-.75Zm5.25.75a.75.75 0 0 0-1.5 0v8.5a.75.75 0 0 0 1.5 0v-8.5Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<>No Image</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{local.isPreview ? (
|
||||
<>
|
||||
<div className="border-l-2 w-1/4 flex flex-col">
|
||||
<a
|
||||
href={`${local.preview.url}`}
|
||||
className={cx(
|
||||
"border-2 rounded m-2 flex flex-row items-center justify-center space-x-4 h-[200px]",
|
||||
css`
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
#d1d1d1 25%,
|
||||
transparent 25%
|
||||
),
|
||||
linear-gradient(-45deg, #d1d1d1 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, #d1d1d1 75%),
|
||||
linear-gradient(-45deg, transparent 75%, #d1d1d1 75%);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
|
||||
`
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cx(
|
||||
"bg-no-repeat bg-contain bg-center rounded flex flex-row flex-grow h-full items-center justify-center space-x-4",
|
||||
css`
|
||||
background-image: url("${local.preview.url}");
|
||||
`
|
||||
)}
|
||||
></div>
|
||||
</a>
|
||||
<p className="text-sm px-2">
|
||||
Dimension:{" "}
|
||||
<span>{`${local.preview.dimension.width} x ${local.preview.dimension.height}`}</span>{" "}
|
||||
</p>
|
||||
<p className="text-sm px-2">
|
||||
File Size:{" "}
|
||||
<span>{getSize(local.preview.details.size)}</span>
|
||||
</p>
|
||||
<p className="text-sm px-2">
|
||||
Last Modified:{" "}
|
||||
<span>
|
||||
{format(
|
||||
new Date(local.preview.details.detail.mtime),
|
||||
"d MMMM yyyy"
|
||||
)}
|
||||
</span>{" "}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex w-full h-full items-center justify-center">
|
||||
<Loading note="gallery" backdrop={false} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -19,5 +19,5 @@
|
|||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {}
|
||||
"dependencies": { "@node-rs/argon2": "^1.5.2" }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,34 +165,25 @@ export const createFrameCors = async (url: string, win?: any) => {
|
|||
};
|
||||
|
||||
export const fetchSendApi = async (
|
||||
_url: string,
|
||||
rawUrl: string,
|
||||
params: any,
|
||||
parentWindow?: any
|
||||
) => {
|
||||
let w: any = typeof window === "object" ? window : globalThis;
|
||||
|
||||
const win = parentWindow || w;
|
||||
let url = _url;
|
||||
const url = new URL(rawUrl);
|
||||
let frm: Awaited<ReturnType<typeof createFrameCors>>;
|
||||
|
||||
const base = `${url.protocol}//${url.host}`;
|
||||
|
||||
if (!win.frmapi) {
|
||||
win.frmapi = {};
|
||||
|
||||
win.frmapi[w.serverurl] = await createFrameCors(w.serverurl, win);
|
||||
win.frmapi[base] = await createFrameCors(base, win);
|
||||
}
|
||||
|
||||
frm = win.frmapi[w.serverurl];
|
||||
frm = win.frmapi[base];
|
||||
|
||||
if (url.startsWith("http")) {
|
||||
const purl = new URL(url);
|
||||
if (!win.frmapi[purl.host]) {
|
||||
win.frmapi[purl.host] = await createFrameCors(
|
||||
`${purl.protocol}//${purl.host}`
|
||||
);
|
||||
}
|
||||
|
||||
frm = win.frmapi[purl.host];
|
||||
url = url.substring(`${purl.protocol}//${purl.host}`.length);
|
||||
}
|
||||
if (!win.apiHeaders) {
|
||||
win.apiHeaders = {};
|
||||
}
|
||||
|
|
@ -204,5 +195,5 @@ export const fetchSendApi = async (
|
|||
});
|
||||
}
|
||||
|
||||
return await frm.send(url, params, win.apiHeaders);
|
||||
return await frm.send(url.pathname, params, win.apiHeaders);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue