feat(api-docs): 更新文档以反映API变化

API文档已更新,包含端点的Base URL更动,以配合新部署的API服务器位置。此外,响应数据的字段名称已根据数据库模型的更新进行调整,确保与返回的数据结构一致。
This commit is contained in:
Wuqiyang312 2024-08-21 09:06:37 +08:00
parent 845876c633
commit 351bf46193
32 changed files with 18376 additions and 5060 deletions

View File

@ -0,0 +1,15 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myValues">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="post_id" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
</profile>
</component>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="file://$PROJECT_DIR$/wqyblog_webui" libraries="{jquery-3.6.0, markdown-it}" />
</component>
</project>

View File

@ -1,14 +1,22 @@
# 博客 RESTful API 文档 # 博客 RESTful API 文档
## 简介 ## 简介
本 API 提供了一个用于管理博客文章的接口,支持使用 Markdown 格式存储内容,并且支持分页查询。后端基于 Go 语言开发,支持 MySQL 和 PostgreSQL 数据库。 本 API 提供了一个用于管理博客文章的接口,支持使用 Markdown 格式存储内容,并且支持分页查询。后端基于 Go 语言开发,支持 MySQL
和 PostgreSQL 数据库。
## 基本信息 ## 基本信息
- **Base URL**: `https://func-e-wiwafluoqc.cn-shanghai.fcapp.run/api` - **Base URL**: `https://wqyblog-cn-api-eqvsnqpylk.cn-shanghai.fcapp.run/api`
- **Content-Type**: `application/json` - **Content-Type**: `application/json`
- **Authorization**: 使用 JWT 进行身份验证。在所有需要身份验证的请求中,必须在请求头中包含 `Authorization: Bearer {token}` - **Authorization**: 使用 JWT
进行身份验证。在所有需要身份验证的请求中,必须在请求头中包含 `Authorization: Bearer {token}`
## 提示
- 在使用此 API 时,请确保使用 HTTPS 来保护数据传输的安全性。
- 案例网站https://wqyblog.cn/
## 测试认证密码 ## 测试认证密码
@ -60,8 +68,8 @@
"CreatedAt": "2024-08-20T12:00:00Z", "CreatedAt": "2024-08-20T12:00:00Z",
"UpdatedAt": "2024-08-20T12:00:00Z", "UpdatedAt": "2024-08-20T12:00:00Z",
"DeletedAt": null, "DeletedAt": null,
"Title": "First Post", "title": "First Post",
"Content": "Markdown content here..." "content": "Markdown content here..."
} }
] ]
} }
@ -107,8 +115,8 @@
"CreatedAt": "2024-08-22T12:00:00Z", "CreatedAt": "2024-08-22T12:00:00Z",
"UpdatedAt": "2024-08-22T12:00:00Z", "UpdatedAt": "2024-08-22T12:00:00Z",
"DeletedAt": null, "DeletedAt": null,
"Title": "New Post Title", "title": "New Post Title",
"Content": "Markdown content here..." "content": "Markdown content here..."
} }
``` ```
- **说明**: 创建一篇新的博文,并返回创建的博文信息。 - **说明**: 创建一篇新的博文,并返回创建的博文信息。
@ -133,8 +141,8 @@
"CreatedAt": "2024-08-20T12:00:00Z", "CreatedAt": "2024-08-20T12:00:00Z",
"UpdatedAt": "2024-08-22T13:00:00Z", "UpdatedAt": "2024-08-22T13:00:00Z",
"DeletedAt": null, "DeletedAt": null,
"Title": "Updated Post Title", "title": "Updated Post Title",
"Content": "Updated markdown content..." "content": "Updated markdown content..."
} }
``` ```
- **说明**: 更新指定 `id` 的博文。如果该 `id` 对应的博文不存在,返回 `404` 错误。 - **说明**: 更新指定 `id` 的博文。如果该 `id` 对应的博文不存在,返回 `404` 错误。
@ -166,7 +174,3 @@ API 在发生错误时会返回以下格式的错误响应:
- **401 Unauthorized**: 需要有效的 JWT 令牌,但令牌未提供或无效。 - **401 Unauthorized**: 需要有效的 JWT 令牌,但令牌未提供或无效。
- **404 Not Found**: 请求的资源不存在(如博文不存在)。 - **404 Not Found**: 请求的资源不存在(如博文不存在)。
- **400 Bad Request**: 请求参数无效或缺失。 - **400 Bad Request**: 请求参数无效或缺失。
## 例外情况
在使用此 API 时,请确保使用 HTTPS 来保护数据传输的安全性。

2
go.mod
View File

@ -3,7 +3,6 @@ module wqyblog_api_go
go 1.23 go 1.23
require ( require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gin-gonic/gin v1.10.0 github.com/gin-gonic/gin v1.10.0
github.com/jinzhu/gorm v1.9.16 github.com/jinzhu/gorm v1.9.16
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
@ -23,6 +22,7 @@ require (
github.com/go-playground/validator/v10 v10.22.0 // indirect github.com/go-playground/validator/v10 v10.22.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/goccy/go-json v0.10.3 // indirect github.com/goccy/go-json v0.10.3 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect

2
go.sum
View File

@ -39,6 +39,8 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=

17
handlers/cors.go Normal file
View File

@ -0,0 +1,17 @@
package handlers
import "github.com/gin-gonic/gin"
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}

View File

@ -2,8 +2,8 @@ package handlers
import ( import (
"errors" "errors"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"net/http" "net/http"
"os" "os"

View File

@ -28,8 +28,11 @@ func GetPosts(c *gin.Context) {
offset := (page - 1) * limit offset := (page - 1) * limit
var posts []models.Post var posts []models.Post
db.DB.Limit(limit).Offset(offset).Find(&posts)
// 按照创建时间降序排序获取帖子
db.DB.Order("created_at desc").Limit(limit).Offset(offset).Find(&posts)
// 返回分页结果
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"page": page, "page": page,
"limit": limit, "limit": limit,

BIN
main

Binary file not shown.

View File

@ -11,6 +11,9 @@ func main() {
r := gin.Default() r := gin.Default()
// 开启跨域
r.Use(handlers.Cors())
r.GET("/", handlers.HalloWorld) r.GET("/", handlers.HalloWorld)
r.POST("/api/auth/login", handlers.Login) r.POST("/api/auth/login", handlers.Login)

View File

@ -1,4 +1,4 @@
POST http://localhost:8080/api/auth/login POST http://localhost:18080/api/auth/login
Content-Type: application/json Content-Type: application/json
{ {
@ -7,13 +7,13 @@ Content-Type: application/json
} }
### ###
POST http://localhost:8080/api/posts POST http://localhost:18080/api/posts
Content-Type: application/json Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzI0MTMxNjIxfQ.NZu5hhl7XjT9BdjIPmBAknekNHIavZYh5Xzz39PUzkk Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzI0MTUzOTMzfQ.3AleOSoNYIEWF1iSVreNKYnetSeawAfjIqAqWWwU058
{ {
"title": "New Post Title", "title": "hello world2",
"content": "Markdown content here..." "content": "# Markdown content here...\n## Heading 2\n\n- hello world2\n- wqyblog.cn"
} }
### ###

View File

@ -4,5 +4,7 @@
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$/wqyblog_webui" /> <content url="file://$MODULE_DIR$/wqyblog_webui" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="jquery-3.6.0" level="application" />
<orderEntry type="library" name="markdown-it" level="application" />
</component> </component>
</module> </module>

View File

@ -1,11 +0,0 @@
# editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

View File

@ -1,194 +0,0 @@
## GITATTRIBUTES FOR WEB PROJECTS
#
# These settings are for any web project.
#
# Details per file setting:
# text These files should be normalized (i.e. convert CRLF to LF).
# binary These files are binary and should be left untouched.
#
# Note that binary is a macro for -text -diff.
######################################################################
## AUTO-DETECT
## Handle line endings automatically for files detected as
## text and leave all files detected as binary untouched.
## This will handle all files NOT defined below.
* text=auto
## SOURCE CODE
*.bat text eol=crlf
*.coffee text
*.css text
*.htm text
*.html text
*.inc text
*.ini text
*.js text
*.json text
*.jsx text
*.less text
*.od text
*.onlydata text
*.php text
*.pl text
*.py text
*.rb text
*.sass text
*.scm text
*.scss text
*.sh text eol=lf
*.sql text
*.styl text
*.tag text
*.ts text
*.tsx text
*.xml text
*.xhtml text
## DOCKER
*.dockerignore text
Dockerfile text
## DOCUMENTATION
*.markdown text
*.md text
*.mdwn text
*.mdown text
*.mkd text
*.mkdn text
*.mdtxt text
*.mdtext text
*.txt text
AUTHORS text
CHANGELOG text
CHANGES text
CONTRIBUTING text
COPYING text
copyright text
*COPYRIGHT* text
INSTALL text
license text
LICENSE text
NEWS text
readme text
*README* text
TODO text
## TEMPLATES
*.dot text
*.ejs text
*.haml text
*.handlebars text
*.hbs text
*.hbt text
*.jade text
*.latte text
*.mustache text
*.njk text
*.phtml text
*.tmpl text
*.tpl text
*.twig text
## LINTERS
.babelrc text
.csslintrc text
.eslintrc text
.htmlhintrc text
.jscsrc text
.jshintrc text
.jshintignore text
.prettierrc text
.stylelintrc text
## CONFIGS
*.bowerrc text
*.cnf text
*.conf text
*.config text
.browserslistrc text
.editorconfig text
.gitattributes text
.gitconfig text
.gitignore text
.htaccess text
*.npmignore text
*.yaml text
*.yml text
browserslist text
Makefile text
makefile text
## HEROKU
Procfile text
.slugignore text
## GRAPHICS
*.ai binary
*.bmp binary
*.eps binary
*.gif binary
*.ico binary
*.jng binary
*.jp2 binary
*.jpg binary
*.jpeg binary
*.jpx binary
*.jxr binary
*.pdf binary
*.png binary
*.psb binary
*.psd binary
*.svg text
*.svgz binary
*.tif binary
*.tiff binary
*.wbmp binary
*.webp binary
## AUDIO
*.kar binary
*.m4a binary
*.mid binary
*.midi binary
*.mp3 binary
*.ogg binary
*.ra binary
## VIDEO
*.3gpp binary
*.3gp binary
*.as binary
*.asf binary
*.asx binary
*.fla binary
*.flv binary
*.m4v binary
*.mng binary
*.mov binary
*.mp4 binary
*.mpeg binary
*.mpg binary
*.ogv binary
*.swc binary
*.swf binary
*.webm binary
## ARCHIVES
*.7z binary
*.gz binary
*.jar binary
*.rar binary
*.tar binary
*.zip binary
## FONTS
*.ttf binary
*.eot binary
*.otf binary
*.woff binary
*.woff2 binary
## EXECUTABLES
*.exe binary
*.pyc binary

View File

@ -1,19 +0,0 @@
Copyright (c) HTML5 Boilerplate
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -15,6 +15,13 @@ html {
text-shadow: none; text-shadow: none;
} }
/* 去除a标签样式 */
a {
color: #323232;
border: none;
text-decoration: none;
}
/* /*
* 更好看的默认水平线 * 更好看的默认水平线
*/ */
@ -92,6 +99,38 @@ body {
color: #333; color: #333;
} }
.header-button-right {
position: absolute;
right: 5vw;
top: 30px;
}
.header-button-left {
position: absolute;
left: 5vw;
top: 30px;
}
/*
main
*/
.main {
flex: 1;
width: 100vw;
display: flex;
flex-direction: row;
}
.main-content {
height: 100%;
flex: 4;
}
.main-sidebar {
flex: 1;
}
/* ========================================================================== /* ==========================================================================
辅助类 辅助类
========================================================================== */ ========================================================================== */

73
wqyblog_webui/edit.html Normal file
View File

@ -0,0 +1,73 @@
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<link rel="stylesheet" href="./css/style.css">
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="icon.png">
<meta name="theme-color" content="#fafafa">
<style>
.main-content {
padding: 0 5%;
}
#post_content {
width: 100%;
flex: 1;
}
#show {
width: 100%;
flex: 1;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="header-title">
编辑
</div>
<div class="header-button-left">
<a href="./index.html">返回</a><br>
<label for="post_title">文章标题:</label><input id="post_title">
</div>
<div class="header-button-right">
<a href="javascript:;" id="save">保存</a>
</div>
</div>
<div class="main">
<div class="main-content">
<p>Markdown内容</p>
<textarea id="post_content" rows="20" cols="100">
</textarea>
</div>
<div style="background-color: #222222;width: 1px;"></div>
<div class="main-content">
<p>Markdown预览</p>
<div id="show">
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/markdown-it@14.1.0/dist/markdown-it.min.js"></script>
<script>
// 检查 jQuery 是否已经加载
window.jQuery || document.write('<script src="./js/vendor/jquery-3.6.0.min.js"><\/script>');
// 检查 markdown-it 是否已经加载
window.markdownit || document.write('<script src="./js/vendor/markdown-it.min.js"><\/script>');
</script>
<script src="./js/const.js"></script>
<script src="./js/edit.js"></script>
</body>
</html>

View File

@ -6,42 +6,70 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title></title> <title></title>
<link rel="stylesheet" href="./css/style.css"> <link rel="stylesheet" href="./css/style.css">
<meta name="description" content="">
<meta property="og:title" content="">
<meta property="og:type" content="">
<meta property="og:url" content="">
<meta property="og:image" content="">
<meta property="og:image:alt" content="">
<link rel="icon" href="/favicon.ico" sizes="any"> <link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/icon.svg" type="image/svg+xml"> <link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="icon.png"> <link rel="apple-touch-icon" href="icon.png">
<link rel="manifest" href="site.webmanifest">
<meta name="theme-color" content="#fafafa"> <meta name="theme-color" content="#fafafa">
<style>
.post-list {
display: flex;
flex-direction: column;
}
.post-item {
min-width: 250px;
width: 100%;
max-width: 600px;
margin: 5px auto;
padding: 12px;
border-radius: 5px;
background: #f5f5f5;
}
.post-title {
font-size: 1.25rem;
font-weight: bold;
}
.post-message {
font-size: 0.8rem;
display: flex;
flex-direction: row;
gap: 16px;
}
</style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<div class="header-title"> <div class="header-title">
WqyBlog.cn WqyBlog 首页
</div> </div>
<div class="login"> <div class="header-button-left"></div>
<a href="login.html">登录</a> <div class="header-button-right">
<a href="./login.html">登录</a>
</div> </div>
</div> </div>
<div class="main"> <div class="main">
<div class="main-content"> <div class="main-content">
<div class="post-list">
</div> </div>
<div class="main-sidebar">
</div> </div>
</div> </div>
</div> </div>
<script src="jquery/dist/jquery.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/markdown-it@14.1.0/dist/markdown-it.min.js"></script>
<script>
// 检查 jQuery 是否已经加载
window.jQuery || document.write('<script src="./js/vendor/jquery-3.6.0.min.js"><\/script>');
// 检查 markdown-it 是否已经加载
window.markdownit || document.write('<script src="./js/vendor/markdown-it.min.js"><\/script>');
</script>
<script src="./js/const.js"></script>
<script src="./js/app.js"></script> <script src="./js/app.js"></script>
</body> </body>

View File

@ -1 +1,82 @@
// app.js wqyblog.cn $(function () {
Cookie(function () {
$('.header-button-left').html(`<a href="./edit.html?s=new">写文章</a>`);
$('.header-button-right').html(`<a id="logout" href="javascript:;">退出登录</a>`);
$('#logout').click(function () {
document.cookie = 'token=;max-age=0'
window.location.href = '/'
})
})
/**
* URL /posts
* 方法 GET
* 参数 page limit
* 响应
* {
* "page": 1,
* "limit": 10,
* "data": [
* {
* "ID": 1,
* "CreatedAt": "2024-08-20T12:00:00Z",
* "UpdatedAt": "2024-08-20T12:00:00Z",
* "DeletedAt": null,
* "Title": "First Post",
* "Content": "Markdown content here..."
* }
* ]
* }
**/
$.ajax({
url: `${baseUrl}/posts`,
type: 'GET',
contentType: 'application/json',
success: function (data) {
if (Cookie()) {
data.data.forEach((item) => {
$('.post-list').append(`<div class="post-item"><div class="post-title">${item.title}</div><div class="post-message"><div>创建时间:${item.CreatedAt}</div><div>更新时间:${item.UpdatedAt}</div><div><a href="javascript:;" class="post_edit" data-post_id="${item.ID}">编辑</a>&nbsp<a href="javascript:;" class="post_delete" data-post_id="${item.ID}">删除</a></div></div><div class="post-content">${md.render(item.content)}</div></div>`);
})
$('.post_edit').click(function () {
// 获取post_id
let post_id = $(this).data('post_id')
window.location.href = '/edit.html?s=old&id=' + post_id
})
$('.post_delete').click(function () {
// 获取post_id
let post_id = $(this).data('post_id')
// 询问是否删除
if (confirm('确定删除吗?')) {
$.ajax({
url: `${baseUrl}/posts/${post_id}`,
type: 'DELETE',
contentType: 'application/json',
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', 'Bearer ' + document.cookie.split('token=')[1].split(';')[0])
},
success: function (data) {
alert('删除成功')
window.location.reload()
},
error: function (xhr, status, error) {
if (xhr.status === 401) {
alert('请先登录')
window.location.href = '/login.html'
} else {
alert('删除失败')
}
}
})
}
})
return
}
data.data.forEach((item) => {
$('.post-list').append(`<div class="post-item"><div class="post-title">${item.title}</div><div class="post-message"><div>创建时间:${item.CreatedAt}</div><div>更新时间:${item.UpdatedAt}</div></div><div class="post-content">${md.render(item.content)}</div></div>`);
})
},
error: function (xhr, status, error) {
}
})
})

13
wqyblog_webui/js/const.js Normal file
View File

@ -0,0 +1,13 @@
const baseUrl = 'https://wqyblog-cn-api-eqvsnqpylk.cn-shanghai.fcapp.run/api'
const md = window.markdownit();
function Cookie(fn = () => {
console.log("yes")
}) {
// 检查cookie 是否有token
if (document.cookie.indexOf('token') !== -1) {
fn();
return true
}
return false
}

78
wqyblog_webui/js/edit.js Normal file
View File

@ -0,0 +1,78 @@
$(function () {
if (!Cookie()){
window.location.href = `/index.html`
}
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('s') === 'new') {
$('#post_title').val('')
$('#post_content').val('')
}
if (urlParams.get('s') === 'old') {
$.ajax({
url: `${baseUrl}/posts/${urlParams.get('id')}`,
type: 'GET',
contentType: 'application/json',
success: function (data) {
$('#post_title').val(data.title)
$('#post_content').val(data.content)
$('#show').html(md.render(data.content));
}
})
}
$('#post_content').on('input', function () {
// 获取输入框的值
let content = $(this).val()
$('#show').html(md.render(content));
})
$('#save').click(function () {
// 获取输入框的值
let title = $('#post_title').val()
let content = $('#post_content').val()
if (title === '' || content === '') {
alert('请输入标题和内容')
return
}
if (urlParams.get('s') === 'new') {
$.ajax({
url: `${baseUrl}/posts`,
type: 'POST',
data: JSON.stringify({
title: title,
content: content
}),
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', 'Bearer ' + document.cookie.split('token=')[1].split(';')[0])
},
contentType: 'application/json',
success: function (data) {
alert('保存成功')
window.location.href = `/index.html`
},
error: function (data) {
alert('保存失败')
}
})
}
if (urlParams.get('s') === 'old') {
$.ajax({
url: `${baseUrl}/posts/${urlParams.get('id')}`,
type: 'PUT',
data: JSON.stringify({
title: title,
content: content
}),
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', 'Bearer ' + document.cookie.split('token=')[1].split(';')[0])
},
contentType: 'application/json',
success: function (data) {
alert('保存成功')
window.location.href = `/index.html`
},
error: function (data) {
}
})
}
})
})

35
wqyblog_webui/js/login.js Normal file
View File

@ -0,0 +1,35 @@
$(function () {
$('#login-form').submit(function (e) {
e.preventDefault()
/**
* URL https://func-e-wiwafluoqc.cn-shanghai.fcapp.run/api/auth/login
* 方法 POST
* 请求体:
* {
* "username": "admin",
* "password": "your_secure_password"
* }
**/
const username = $('#username').val()
const password = $('#password').val()
$.ajax({
url: `${baseUrl}/auth/login`,
type: 'POST',
data: JSON.stringify({
username,
password
}),
contentType: 'application/json',
success: function (data) {
// cookie 1小时
document.cookie = `token=${data.token};max-age=3600`
window.location.href = '/'
},
error: function (xhr, status, error) {
$('.login').html(`<h1>登录失败</h1><p>${jQuery.parseJSON(xhr.responseText).error}</p><p><a href="./login.html">重新登录</a></p>`)
}
})
$('.login').html(`<h1>正在登录中……</h1>`)
return false
})
})

10881
wqyblog_webui/js/vendor/jquery-3.6.0.min.js vendored Normal file

File diff suppressed because it is too large Load Diff

6963
wqyblog_webui/js/vendor/markdown-it.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -3,24 +3,106 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>登录</title> <title></title>
<link rel="stylesheet" href="./css/style.css"> <link rel="stylesheet" href="./css/style.css">
<meta name="description" content="">
<meta property="og:title" content="">
<meta property="og:type" content="">
<meta property="og:url" content="">
<meta property="og:image" content="">
<meta property="og:image:alt" content="">
<link rel="icon" href="/favicon.ico" sizes="any"> <link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/icon.svg" type="image/svg+xml"> <link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="icon.png"> <link rel="apple-touch-icon" href="icon.png">
<link rel="manifest" href="site.webmanifest">
<meta name="theme-color" content="#fafafa"> <meta name="theme-color" content="#fafafa">
<style>
.login {
flex: 1;
flex-direction: column;
display: flex;
justify-content: center;
align-items: center;
}
#login-form {
min-width: 250px;
width: 50vw;
max-width: 600px;
}
.form-group {
margin: 24px 0;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.form-group label {
font-size: 1.25rem;
flex: 1;
}
.form-group input {
font-size: 1.25rem;
flex: 4;
/* 删除边框\圆角\背景 */
border: none;
border-radius: 0;
background: none;
padding-inline: 10px;
border-bottom: #333333 1px solid;
}
.form-group button {
font-size: 1.15rem;
padding: 2px 20px;
/* 删除边框\圆角\背景 */
border: none;
border-radius: 0;
background: none;
border-bottom: #333333 1px solid;
}
.form-group button:hover {
font-weight: bold;
}
</style>
</head> </head>
<body> <body>
<div class="container">
<div class="header">
<div class="header-title">
WqyBlog 登录
</div>
<div class="header-button-left">
<a href="./index.html">返回</a>
</div>
</div>
<div class="main">
<div class="login">
<form id="login-form">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username" placeholder="请输入用户名">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" name="password" placeholder="请输入密码">
</div>
<div class="form-group">
<button type="submit">登录</button>
</div>
</form>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
// 检查 jQuery 是否已经加载
window.jQuery || document.write('<script src="./js/vendor/jquery-3.6.0.min.js"><\/script>');
</script>
<script src="./js/const.js"></script>
<script src="./js/login.js"></script>
</body> </body>
</html> </html>

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +0,0 @@
{
"name": " ",
"version": "0.0.1",
"description": "",
"private": true,
"keywords": [
""
],
"license": "",
"author": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack serve --open --config webpack.config.dev.js",
"build": "webpack --config webpack.config.prod.js"
},
"devDependencies": {
"copy-webpack-plugin": "^11.0.0",
"html-webpack-plugin": "^5.6.0",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4",
"webpack-merge": "^5.10.0"
},
"dependencies": {
"jquery": "^3.7.1"
}
}

View File

@ -1,5 +0,0 @@
# www.robotstxt.org/
# Allow crawling of all content
User-agent: *
Disallow:

View File

@ -1,12 +0,0 @@
{
"short_name": "",
"name": "",
"icons": [{
"src": "icon.png",
"type": "image/png",
"sizes": "192x192"
}],
"start_url": "/?utm_source=homescreen",
"background_color": "#fafafa",
"theme_color": "#fafafa"
}

View File

@ -1,12 +0,0 @@
const path = require('path');
module.exports = {
entry: {
app: './js/app.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
clean: true,
filename: './js/app.js',
},
};

View File

@ -1,13 +0,0 @@
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
liveReload: true,
hot: true,
open: true,
static: ['./'],
},
});

View File

@ -1,26 +0,0 @@
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = merge(common, {
mode: 'production',
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
}),
new CopyPlugin({
patterns: [
{ from: 'img', to: 'img' },
{ from: 'css', to: 'css' },
{ from: 'js/vendor', to: 'js/vendor' },
{ from: 'icon.svg', to: 'icon.svg' },
{ from: 'favicon.ico', to: 'favicon.ico' },
{ from: 'robots.txt', to: 'robots.txt' },
{ from: 'icon.png', to: 'icon.png' },
{ from: '404.html', to: '404.html' },
{ from: 'site.webmanifest', to: 'site.webmanifest' },
],
}),
],
});