feat: license setting
This commit is contained in:
@@ -13,7 +13,8 @@ import {
|
|||||||
CreateAdminDTO,
|
CreateAdminDTO,
|
||||||
SMTPConfig,
|
SMTPConfig,
|
||||||
AgoraConfig,
|
AgoraConfig,
|
||||||
GithubAuthConfig
|
GithubAuthConfig,
|
||||||
|
LicenseResponse
|
||||||
} from "../../types/server";
|
} from "../../types/server";
|
||||||
|
|
||||||
const defaultExpireDuration = 7 * 24 * 60 * 60;
|
const defaultExpireDuration = 7 * 24 * 60 * 60;
|
||||||
@@ -192,6 +193,21 @@ export const serverApi = createApi({
|
|||||||
query: () => ({
|
query: () => ({
|
||||||
url: `/admin/system/initialized`
|
url: `/admin/system/initialized`
|
||||||
})
|
})
|
||||||
|
}),
|
||||||
|
|
||||||
|
checkLicense: builder.mutation<LicenseResponse, string>({
|
||||||
|
query: (license) => ({
|
||||||
|
url: "/license/check",
|
||||||
|
method: "POST",
|
||||||
|
body: { license }
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
upsertLicense: builder.mutation<boolean, string>({
|
||||||
|
query: (license) => ({
|
||||||
|
url: "/license/save",
|
||||||
|
method: "POST",
|
||||||
|
body: { license }
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -223,5 +239,7 @@ export const {
|
|||||||
useGetThirdPartySecretQuery,
|
useGetThirdPartySecretQuery,
|
||||||
useUpdateThirdPartySecretMutation,
|
useUpdateThirdPartySecretMutation,
|
||||||
useCreateAdminMutation,
|
useCreateAdminMutation,
|
||||||
useGetInitializedQuery
|
useGetInitializedQuery,
|
||||||
|
useUpsertLicenseMutation,
|
||||||
|
useCheckLicenseMutation
|
||||||
} = serverApi;
|
} = serverApi;
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { useCheckLicenseMutation, useUpsertLicenseMutation } from "../../app/services/server";
|
||||||
|
|
||||||
|
const useLicense = (license?: string) => {
|
||||||
|
const [check, { data, isLoading: isChecking, isSuccess: checked }] = useCheckLicenseMutation();
|
||||||
|
const [upsert, { isSuccess: upserted, isLoading: upserting }] = useUpsertLicenseMutation();
|
||||||
|
const checkLicense = (l: string) => {
|
||||||
|
check(l);
|
||||||
|
};
|
||||||
|
const upsertLicense = async (l: string) => {
|
||||||
|
// check first
|
||||||
|
const resp = await check(l);
|
||||||
|
if ("data" in resp && resp.data.sign) {
|
||||||
|
return await upsert(l);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
if (license) {
|
||||||
|
checkLicense(license);
|
||||||
|
}
|
||||||
|
}, [license]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
validInfo: data,
|
||||||
|
checked,
|
||||||
|
checking: isChecking,
|
||||||
|
upserting,
|
||||||
|
upserted,
|
||||||
|
checkLicense,
|
||||||
|
upsertLicense
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useLicense;
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import { ChangeEvent, useState } from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import Tippy from "@tippyjs/react";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
import { hideAll } from "tippy.js";
|
||||||
|
import Textarea from "../../common/component/styled/Textarea";
|
||||||
|
import Button from "../../common/component/styled/Button";
|
||||||
|
import useLicense from "../../common/hook/useLicense";
|
||||||
|
|
||||||
|
const StyledConfirm = styled.div`
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid orange;
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
width: 250px;
|
||||||
|
.tip {
|
||||||
|
color: orange;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.btns {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const Styled = styled.div`
|
||||||
|
max-width: 500px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 15px;
|
||||||
|
> .input {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function License() {
|
||||||
|
const { checking, upserting, upsertLicense } = useLicense();
|
||||||
|
const [license, setLicense] = useState("");
|
||||||
|
const handleUpsert = async () => {
|
||||||
|
if (!license) {
|
||||||
|
toast.error("License Empty");
|
||||||
|
hideAll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const success = await upsertLicense(license);
|
||||||
|
if (!success) {
|
||||||
|
toast.error("Invalid License");
|
||||||
|
hideAll();
|
||||||
|
} else {
|
||||||
|
toast.success("License Updated!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleLicenseInput = (evt: ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
|
setLicense(evt.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const disableBtn = checking || upserting || !license;
|
||||||
|
return (
|
||||||
|
<Styled>
|
||||||
|
<div className="input">
|
||||||
|
<Textarea rows={10} id="license" value={license} onChange={handleLicenseInput} />
|
||||||
|
</div>
|
||||||
|
<Tippy
|
||||||
|
interactive
|
||||||
|
placement="bottom-start"
|
||||||
|
trigger="click"
|
||||||
|
content={
|
||||||
|
<StyledConfirm>
|
||||||
|
<div className="tip">Are you sure to update License?</div>
|
||||||
|
<div className="btns">
|
||||||
|
<Button onClick={() => hideAll()} className="cancel small">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button disabled={disableBtn} className="small danger" onClick={handleUpsert}>
|
||||||
|
Yes
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</StyledConfirm>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button disabled={disableBtn}>Save License</Button>
|
||||||
|
</Tippy>
|
||||||
|
</Styled>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import Logins from "./config/Logins";
|
|||||||
import ConfigFirebase from "./config/Firebase";
|
import ConfigFirebase from "./config/Firebase";
|
||||||
import ConfigSMTP from "./config/SMTP";
|
import ConfigSMTP from "./config/SMTP";
|
||||||
import APIConfig from "./APIConfig";
|
import APIConfig from "./APIConfig";
|
||||||
|
import License from "./License";
|
||||||
import ManageMembers from "../../common/component/ManageMembers";
|
import ManageMembers from "../../common/component/ManageMembers";
|
||||||
import FAQ from "../../common/component/FAQ";
|
import FAQ from "../../common/component/FAQ";
|
||||||
import ConfigAgora from "./config/Agora";
|
import ConfigAgora from "./config/Agora";
|
||||||
@@ -63,6 +64,11 @@ const navs = [
|
|||||||
name: "api",
|
name: "api",
|
||||||
title: "Third-party APP",
|
title: "Third-party APP",
|
||||||
component: <APIConfig />
|
component: <APIConfig />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "license",
|
||||||
|
title: "License",
|
||||||
|
component: <License />
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
admin: true
|
admin: true
|
||||||
|
|||||||
@@ -58,6 +58,12 @@ export interface LoginConfig {
|
|||||||
metamask: boolean;
|
metamask: boolean;
|
||||||
third_party: boolean;
|
third_party: boolean;
|
||||||
}
|
}
|
||||||
|
export interface LicenseResponse {
|
||||||
|
domain: string;
|
||||||
|
created_at: string;
|
||||||
|
expired_at: string;
|
||||||
|
sign: boolean;
|
||||||
|
}
|
||||||
export interface CreateAdminDTO extends Pick<User, "email" | "name" | "gender"> {
|
export interface CreateAdminDTO extends Pick<User, "email" | "name" | "gender"> {
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user