feat(api-docs): 更新文档以反映API变化
API文档已更新,包含端点的Base URL更动,以配合新部署的API服务器位置。此外,响应数据的字段名称已根据数据库模型的更新进行调整,确保与返回的数据结构一致。
This commit is contained in:
parent
845876c633
commit
351bf46193
|
@ -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>
|
|
@ -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>
|
32
README.md
32
README.md
|
@ -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`
|
||||
- **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",
|
||||
"UpdatedAt": "2024-08-20T12:00:00Z",
|
||||
"DeletedAt": null,
|
||||
"Title": "First Post",
|
||||
"Content": "Markdown content here..."
|
||||
"title": "First Post",
|
||||
"content": "Markdown content here..."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -107,8 +115,8 @@
|
|||
"CreatedAt": "2024-08-22T12:00:00Z",
|
||||
"UpdatedAt": "2024-08-22T12:00:00Z",
|
||||
"DeletedAt": null,
|
||||
"Title": "New Post Title",
|
||||
"Content": "Markdown content here..."
|
||||
"title": "New Post Title",
|
||||
"content": "Markdown content here..."
|
||||
}
|
||||
```
|
||||
- **说明**: 创建一篇新的博文,并返回创建的博文信息。
|
||||
|
@ -133,8 +141,8 @@
|
|||
"CreatedAt": "2024-08-20T12:00:00Z",
|
||||
"UpdatedAt": "2024-08-22T13:00:00Z",
|
||||
"DeletedAt": null,
|
||||
"Title": "Updated Post Title",
|
||||
"Content": "Updated markdown content..."
|
||||
"title": "Updated Post Title",
|
||||
"content": "Updated markdown content..."
|
||||
}
|
||||
```
|
||||
- **说明**: 更新指定 `id` 的博文。如果该 `id` 对应的博文不存在,返回 `404` 错误。
|
||||
|
@ -166,7 +174,3 @@ API 在发生错误时会返回以下格式的错误响应:
|
|||
- **401 Unauthorized**: 需要有效的 JWT 令牌,但令牌未提供或无效。
|
||||
- **404 Not Found**: 请求的资源不存在(如博文不存在)。
|
||||
- **400 Bad Request**: 请求参数无效或缺失。
|
||||
|
||||
## 例外情况
|
||||
|
||||
在使用此 API 时,请确保使用 HTTPS 来保护数据传输的安全性。
|
||||
|
|
2
go.mod
2
go.mod
|
@ -3,7 +3,6 @@ module wqyblog_api_go
|
|||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
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-sql-driver/mysql v1.8.1 // 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/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -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/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
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/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@ package handlers
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
|
|
@ -28,8 +28,11 @@ func GetPosts(c *gin.Context) {
|
|||
offset := (page - 1) * limit
|
||||
|
||||
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{
|
||||
"page": page,
|
||||
"limit": limit,
|
||||
|
|
3
main.go
3
main.go
|
@ -11,6 +11,9 @@ func main() {
|
|||
|
||||
r := gin.Default()
|
||||
|
||||
// 开启跨域
|
||||
r.Use(handlers.Cors())
|
||||
|
||||
r.GET("/", handlers.HalloWorld)
|
||||
|
||||
r.POST("/api/auth/login", handlers.Login)
|
||||
|
|
10
text.http
10
text.http
|
@ -1,4 +1,4 @@
|
|||
POST http://localhost:8080/api/auth/login
|
||||
POST http://localhost:18080/api/auth/login
|
||||
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
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzI0MTMxNjIxfQ.NZu5hhl7XjT9BdjIPmBAknekNHIavZYh5Xzz39PUzkk
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzI0MTUzOTMzfQ.3AleOSoNYIEWF1iSVreNKYnetSeawAfjIqAqWWwU058
|
||||
|
||||
{
|
||||
"title": "New Post Title",
|
||||
"content": "Markdown content here..."
|
||||
"title": "hello world2",
|
||||
"content": "# Markdown content here...\n## Heading 2\n\n- hello world2\n- wqyblog.cn"
|
||||
}
|
||||
|
||||
###
|
||||
|
|
|
@ -4,5 +4,7 @@
|
|||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$/wqyblog_webui" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="jquery-3.6.0" level="application" />
|
||||
<orderEntry type="library" name="markdown-it" level="application" />
|
||||
</component>
|
||||
</module>
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -15,6 +15,13 @@ html {
|
|||
text-shadow: none;
|
||||
}
|
||||
|
||||
/* 去除a标签样式 */
|
||||
a {
|
||||
color: #323232;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* 更好看的默认水平线
|
||||
*/
|
||||
|
@ -92,6 +99,38 @@ body {
|
|||
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;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
辅助类
|
||||
========================================================================== */
|
||||
|
|
|
@ -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>
|
|
@ -6,42 +6,70 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title></title>
|
||||
<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="/icon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="icon.png">
|
||||
|
||||
<link rel="manifest" href="site.webmanifest">
|
||||
<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>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="header-title">
|
||||
WqyBlog.cn
|
||||
WqyBlog 首页
|
||||
</div>
|
||||
<div class="login">
|
||||
<a href="login.html">登录</a>
|
||||
<div class="header-button-left"></div>
|
||||
<div class="header-button-right">
|
||||
<a href="./login.html">登录</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="main-content">
|
||||
|
||||
</div>
|
||||
<div class="main-sidebar">
|
||||
|
||||
<div class="post-list">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="jquery/dist/jquery.js"></script>
|
||||
</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/app.js"></script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -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> <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) {
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
|
@ -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
|
||||
})
|
||||
})
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -3,24 +3,106 @@
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>登录</title>
|
||||
<title></title>
|
||||
<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="/icon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="icon.png">
|
||||
|
||||
<link rel="manifest" href="site.webmanifest">
|
||||
<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>
|
||||
<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>
|
||||
</html>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
# www.robotstxt.org/
|
||||
|
||||
# Allow crawling of all content
|
||||
User-agent: *
|
||||
Disallow:
|
|
@ -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"
|
||||
}
|
|
@ -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',
|
||||
},
|
||||
};
|
|
@ -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: ['./'],
|
||||
},
|
||||
});
|
|
@ -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' },
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
Loading…
Reference in New Issue