web: update restful api

This commit is contained in:
steven 2021-12-09 21:45:48 +08:00
parent b8c01524c5
commit bdaeb3a68b
14 changed files with 75 additions and 178 deletions

View file

@ -7,13 +7,11 @@
"serve": "vite preview"
},
"dependencies": {
"prismjs": "^1.25.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"tiny-undo": "^0.0.8"
},
"devDependencies": {
"@types/prismjs": "^1.16.6",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.2",
"@vitejs/plugin-react": "^1.0.0",

View file

@ -6,8 +6,8 @@ import "../less/change-password-dialog.less";
interface Props extends DialogProps {}
const BindWxUserIdDialog: React.FC<Props> = ({ destroy }: Props) => {
const [wxUserId, setWxUserId] = useState("");
const BindWxOpenIdDialog: React.FC<Props> = ({ destroy }: Props) => {
const [wxOpenId, setWxOpenId] = useState("");
useEffect(() => {
// do nth
@ -17,19 +17,19 @@ const BindWxUserIdDialog: React.FC<Props> = ({ destroy }: Props) => {
destroy();
};
const handleWxUserIdChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const handleWxOpenIdChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const text = e.target.value as string;
setWxUserId(text);
setWxOpenId(text);
};
const handleSaveBtnClick = async () => {
if (wxUserId === "") {
if (wxOpenId === "") {
toastHelper.error("微信 id 不能为空");
return;
}
try {
await userService.updateWxUserId(wxUserId);
await userService.updateWxOpenId(wxOpenId);
userService.doSignIn();
toastHelper.info("绑定成功!");
handleCloseBtnClick();
@ -51,8 +51,8 @@ const BindWxUserIdDialog: React.FC<Props> = ({ destroy }: Props) => {
<strong>OpenID</strong>
</p>
<label className="form-label input-form-label">
<span className={"normal-text " + (wxUserId === "" ? "" : "not-null")}> OpenID</span>
<input type="text" value={wxUserId} onChange={handleWxUserIdChanged} />
<span className={"normal-text " + (wxOpenId === "" ? "" : "not-null")}> OpenID</span>
<input type="text" value={wxOpenId} onChange={handleWxOpenIdChanged} />
</label>
<div className="btns-container">
<span className="btn cancel-btn" onClick={handleCloseBtnClick}>
@ -67,13 +67,13 @@ const BindWxUserIdDialog: React.FC<Props> = ({ destroy }: Props) => {
);
};
function showBindWxUserIdDialog() {
function showBindWxOpenIdDialog() {
showDialog(
{
className: "bind-wxid-dialog",
},
BindWxUserIdDialog
BindWxOpenIdDialog
);
}
export default showBindWxUserIdDialog;
export default showBindWxOpenIdDialog;

View file

@ -6,7 +6,7 @@ import { validate, ValidatorConfig } from "../helpers/validator";
import Only from "./common/OnlyWhen";
import toastHelper from "./Toast";
import showChangePasswordDialog from "./ChangePasswordDialog";
import showBindWxUserIdDialog from "./BindWxUserIdDialog";
import showBindWxOpenIdDialog from "./BindWxOpenIdDialog";
import "../less/my-account-section.less";
const validateConfig: ValidatorConfig = {
@ -91,7 +91,7 @@ const MyAccountSection: React.FC<Props> = () => {
const handleUnbindWxBtnClick = async () => {
if (showConfirmUnbindWxBtn) {
try {
await userService.updateWxUserId("");
await userService.updateWxOpenId("");
await userService.doSignIn();
} catch (error: any) {
toastHelper.error(error.message);
@ -157,7 +157,7 @@ const MyAccountSection: React.FC<Props> = () => {
<p className="title-text"></p>
<label className="form-label input-form-label">
<span className="normal-text"> OpenID</span>
{user.wxUserId ? (
{user.wxOpenId ? (
<>
<span className="value-text">************</span>
<span
@ -174,7 +174,7 @@ const MyAccountSection: React.FC<Props> = () => {
<span
className="btn-text bind-btn"
onClick={() => {
showBindWxUserIdDialog();
showBindWxOpenIdDialog();
}}
>
ID

View file

@ -1,143 +1,123 @@
import utils from "./utils";
type ResponseType<T = unknown> = {
succeed: boolean;
status: number;
message: string;
data: T;
};
async function get<T>(url: string): Promise<ResponseType<T>> {
const response = await fetch(url, {
method: "GET",
});
const resData = (await response.json()) as ResponseType<T>;
async function request<T>(method: string, url: string, data?: BasicType): Promise<ResponseType<T>> {
const requestConfig: RequestInit = {
method,
};
if (!resData.succeed) {
throw resData;
}
return resData;
}
async function post<T>(url: string, data?: BasicType): Promise<ResponseType<T>> {
const response = await fetch(url, {
method: "POST",
headers: {
if (method !== "GET") {
requestConfig.headers = {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const resData = (await response.json()) as ResponseType<T>;
if (!resData.succeed) {
throw resData;
};
if (data !== null) {
}
requestConfig.body = JSON.stringify(data);
}
return resData;
const response = await fetch(url, requestConfig);
const responseData = (await response.json()) as ResponseType<T>;
if (!responseData.succeed) {
throw responseData;
}
return responseData;
}
namespace api {
export function getUserInfo() {
return get<Model.User>("/api/user/me");
return request<Model.User>("GET", "/api/user/me");
}
export function signin(username: string, password: string) {
return post("/api/user/signin", { username, password });
return request("POST", "/api/auth/signin", { username, password });
}
export function signup(username: string, password: string) {
return post("/api/user/signup", { username, password });
return request("POST", "/api/auth/signup", { username, password });
}
export function signout() {
return post("/api/user/signout");
return request("POST", "/api/auth/signout");
}
export function checkUsernameUsable(username: string) {
return get<boolean>("/api/user/checkusername?username=" + username);
return request<boolean>("POST", "/api/user/checkusername", { username });
}
export function checkPasswordValid(password: string) {
return post<boolean>("/api/user/checkpassword", { password });
return request<boolean>("POST", "/api/user/validpassword", { password });
}
export function updateUserinfo(username?: string, password?: string, githubName?: string, wxUserId?: string) {
return post("/api/user/update", {
export function updateUserinfo(username?: string, password?: string, githubName?: string, wxOpenId?: string) {
return request("PATCH", "/api/user/me", {
username,
password,
githubName,
wxUserId,
wxOpenId,
});
}
export function getMyMemos() {
return get<Model.Memo[]>("/api/memo/all");
return request<Model.Memo[]>("GET", "/api/memo/all");
}
export function getMyDeletedMemos() {
return get<Model.Memo[]>("/api/memo/deleted");
return request<Model.Memo[]>("GET", "/api/memo/all?deleted=true");
}
export function createMemo(content: string) {
return post<Model.Memo>("/api/memo/new", { content });
return request<Model.Memo>("PUT", "/api/memo/", { content });
}
export function getMemoById(id: string) {
return get<Model.Memo>("/api/memo/?id=" + id);
export function updateMemo(memoId: string, content: string) {
return request<Model.Memo>("PATCH", `/api/memo/${memoId}`, { content });
}
export function hideMemo(memoId: string) {
return post("/api/memo/hide", {
memoId,
return request("PATCH", `/api/memo/${memoId}`, {
deletedAt: utils.getDateTimeString(Date.now()),
});
}
export function restoreMemo(memoId: string) {
return post("/api/memo/restore", {
memoId,
return request("PATCH", `/api/memo/${memoId}`, {
deletedAt: "",
});
}
export function deleteMemo(memoId: string) {
return post("/api/memo/delete", {
memoId,
});
}
export function updateMemo(memoId: string, content: string) {
return post<Model.Memo>("/api/memo/update", { memoId, content });
}
export function getLinkedMemos(memoId: string) {
return get<Model.Memo[]>("/api/memo/linked?memoId=" + memoId);
}
export function removeGithubName() {
return post("/api/user/updategh", { githubName: "" });
return request("DELETE", `/api/memo/${memoId}`);
}
export function getMyQueries() {
return get<Model.Query[]>("/api/query/all");
return request<Model.Query[]>("GET", "/api/query/all");
}
export function createQuery(title: string, querystring: string) {
return post<Model.Query>("/api/query/new", { title, querystring });
return request<Model.Query>("PUT", "/api/query/", { title, querystring });
}
export function updateQuery(queryId: string, title: string, querystring: string) {
return post<Model.Query>("/api/query/update", { queryId, title, querystring });
return request<Model.Query>("PATCH", `/api/query/${queryId}`, { title, querystring });
}
export function deleteQueryById(queryId: string) {
return post("/api/query/delete", { queryId });
return request("DELETE", `/api/query/${queryId}`);
}
export function pinQuery(queryId: string) {
return post("/api/query/pin", { queryId });
return request("PATCH", `/api/query/${queryId}`, { pinnedAt: utils.getDateTimeString(Date.now()) });
}
export function unpinQuery(queryId: string) {
return post("/api/query/unpin", { queryId });
return request("PATCH", `/api/query/${queryId}`, { pinnedAt: "" });
}
}

View file

@ -5,8 +5,6 @@
* - /
* - TODO;
*/
import Prism from "prismjs";
const CODE_BLOCK_REG = /```([\s\S]*?)```/g;
const BOLD_TEXT_REG = /\*\*(.+?)\*\*/g;
const EM_TEXT_REG = /\*(.+?)\*/g;
@ -15,46 +13,10 @@ const DONE_BLOCK_REG = /\[x\] /g;
const DOT_LI_REG = /[*] /g;
const NUM_LI_REG = /(\d+)\. /g;
const getCodeLanguage = (codeStr: string): string => {
const execRes = /^\w+/g.exec(codeStr);
if (execRes !== null) {
return execRes[0];
}
return "javascript";
};
const parseCodeToPrism = (codeStr: string): string => {
return codeStr.replace(CODE_BLOCK_REG, (_, matchedStr): string => {
const lang = getCodeLanguage(matchedStr);
let convertedStr = matchedStr
.replace(lang, "")
.replace(/<p>/g, "")
.replace(/<\/p>/g, "\r\n")
.replace(/<br>/g, "\r\n")
.replace(/&nbsp;/g, " ");
// 特定语言处理
switch (lang) {
case "html":
convertedStr = convertedStr.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
}
try {
const resultStr = Prism.highlight(convertedStr, Prism.languages[lang], lang);
return `<pre>${resultStr}</pre>`;
} catch (error) {
// do nth
}
return `<pre>${codeStr}</pre>`;
});
};
const parseMarkedToHtml = (markedStr: string): string => {
const htmlText = parseCodeToPrism(markedStr)
.replace(DOT_LI_REG, "<span class='counter-block'>◦</span>")
const htmlText = markedStr
.replace(CODE_BLOCK_REG, "<pre lang=''>$1</pre>")
.replace(DOT_LI_REG, "<span class='counter-block'>•</span>")
.replace(NUM_LI_REG, "<span class='counter-block'>$1.</span>")
.replace(TODO_BLOCK_REG, "<span class='todo-block' data-type='todo'>⬜</span>")
.replace(DONE_BLOCK_REG, "<span class='todo-block' data-type='done'>✅</span>")

View file

@ -54,7 +54,7 @@
.todo-block {
display: inline-block;
text-align: center;
width: 2rem;
width: 1.4rem;
.mono-font-family();
}

View file

@ -119,31 +119,6 @@
}
}
> .tip-text {
.flex(row, flex-start, center);
border-top: 2px solid lightgray;
color: gray;
padding-top: 16px;
width: 100%;
font-size: 12px;
line-height: 30px;
> .btn {
width: 86px;
color: @text-green;
border-radius: 4px;
background-color: @bg-lightgray;
> .icon-text {
margin-right: 4px;
}
&:hover {
opacity: 0.8;
}
}
}
> .quickly-btns-container {
.flex(column, flex-start, flex-start);
width: 100%;

View file

@ -154,14 +154,6 @@ const Signin: React.FC<Props> = () => {
</div>
</div>
<p className="tip-text">
<br />
<span className="btn" onClick={handleAboutBtnClick}>
<span className="icon-text">🤠</span>
</span>
</p>
</>
) : (
<>

View file

@ -117,6 +117,11 @@ class MemoService {
});
}
public async getLinkedMemos(memoId: string): Promise<Model.Memo[]> {
const { memos } = this.getState();
return memos.filter((m) => m.content.includes(memoId));
}
public async createMemo(text: string): Promise<Model.Memo> {
const { data: memo } = await api.createMemo(text);
return memo;
@ -126,11 +131,6 @@ class MemoService {
const { data: memo } = await api.updateMemo(memoId, text);
return memo;
}
public async getLinkedMemos(memoId: string): Promise<Model.Memo[]> {
const { data } = await api.getLinkedMemos(memoId);
return data;
}
}
const memoService = new MemoService();

View file

@ -51,8 +51,8 @@ class UserService {
await api.updateUserinfo(undefined, password);
}
public async updateWxUserId(wxUserId: string): Promise<void> {
await api.updateUserinfo(undefined, undefined, undefined, wxUserId);
public async updateWxOpenId(wxOpenId: string): Promise<void> {
await api.updateUserinfo(undefined, undefined, undefined, wxOpenId);
}
}

View file

@ -8,7 +8,7 @@ declare namespace Model {
interface User extends BaseModel {
username: string;
githubName?: string;
wxUserId?: string;
wxOpenId?: string;
}
interface Memo extends BaseModel {

View file

@ -8,8 +8,8 @@ export default defineConfig({
cors: true,
proxy: {
"/api": {
// target: "http://localhost:8080/",
target: "https://memos.justsven.top/",
target: "http://localhost:8080/",
// target: "https://memos.justsven.top/",
changeOrigin: true,
},
},

View file

@ -260,11 +260,6 @@
estree-walker "^2.0.1"
picomatch "^2.2.2"
"@types/prismjs@^1.16.6":
version "1.16.6"
resolved "http://npm.corp.ebay.com/@types/prismjs/-/prismjs-1.16.6.tgz#377054f72f671b36dbe78c517ce2b279d83ecc40"
integrity sha1-N3BU9y9nGzbb54xRfOKyedg+zEA=
"@types/prop-types@*":
version "15.7.4"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
@ -705,11 +700,6 @@ postcss@^8.3.8:
picocolors "^1.0.0"
source-map-js "^0.6.2"
prismjs@^1.25.0:
version "1.25.0"
resolved "http://npm.corp.ebay.com/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756"
integrity sha1-b4It8b2tllc0sxCzFaIzFc+Zl1Y=
prr@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"