mirror of
https://github.com/usememos/memos.git
synced 2025-01-04 20:41:54 +03:00
web: update restful api
This commit is contained in:
parent
b8c01524c5
commit
bdaeb3a68b
@ -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",
|
||||
|
@ -25,7 +25,7 @@ const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => {
|
||||
<p>You are in charge of your data and customizations.</p>
|
||||
<p>Built with React and Go.</p>
|
||||
<br />
|
||||
<p>Enjoy it and have fun~ </p>
|
||||
<p>Enjoy it and have fun~</p>
|
||||
<hr />
|
||||
<p className="normal-text">
|
||||
Last updated on <span className="pre-text">2021/12/09 10:14:32</span> 🎉
|
||||
|
@ -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;
|
@ -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
|
||||
|
@ -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: "" });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(/ /g, " ");
|
||||
|
||||
// 特定语言处理
|
||||
switch (lang) {
|
||||
case "html":
|
||||
convertedStr = convertedStr.replace(/</g, "<").replace(/>/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>")
|
||||
|
@ -54,7 +54,7 @@
|
||||
.todo-block {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
width: 2rem;
|
||||
width: 1.4rem;
|
||||
.mono-font-family();
|
||||
}
|
||||
|
||||
|
@ -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%;
|
||||
|
@ -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>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
2
web/src/types/models.d.ts
vendored
2
web/src/types/models.d.ts
vendored
@ -8,7 +8,7 @@ declare namespace Model {
|
||||
interface User extends BaseModel {
|
||||
username: string;
|
||||
githubName?: string;
|
||||
wxUserId?: string;
|
||||
wxOpenId?: string;
|
||||
}
|
||||
|
||||
interface Memo extends BaseModel {
|
||||
|
@ -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,
|
||||
},
|
||||
},
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user