feat(api): 添加用户管理和博文管理的RESTful API端点,添加基本的WebUI结构和样式,编写API文档说明,修复用户表密码字段类型
添加了处理用户认证、博文创建、读取、更新和删除的API端点。实现JWT认证机制,支持用户登录并进行相应的权限验证。同时,添加了数据库设置和模型定义,用于用户和博文的数据存储和管理。为项目添加了基本的WebUI结构,包括HTML、CSS和JavaScript文件,用于登录页面和404页面的展示。同时,配置了webui模块的项目文件,如`.editorconfig`和`.gitignore`。编写了API的使用文档,包括API的基本信息、请求和响应示例、错误处理等。支持分页查询、Markdown格式的博文内容存储。在数据库设置中,将用户表的密码字段更改为byte类型,以安全地存储经过bcrypt哈希处理的密码。确保在创建或更新用户时,密码得到正确的处理和存储。
This commit is contained in:
parent
65dd1f3e64
commit
845876c633
|
@ -0,0 +1,7 @@
|
||||||
|
JWT_SECRET=8ThbmiozWCLvw3WCumWFEZW6RUxNZVmXjcETTaAotjX6Fh5tXxzPvHpZH12FUGUjTitsfB0vXEK1GGKcRUb2hAaTAisUehR6W7Ln
|
||||||
|
DB_TYPE=mysql # 可以是 "postgresql" 或 "mysql"
|
||||||
|
DB_HOST=wuqingyuan.mysql.rds.aliyuncs.com
|
||||||
|
DB_PORT=3306 # 对于 MySQL,这里通常是 3306
|
||||||
|
DB_USER=blogdb
|
||||||
|
DB_PASSWORD=blogdb@123321
|
||||||
|
DB_NAME=blogdb
|
|
@ -3,6 +3,7 @@
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/wqyblog_api_go.iml" filepath="$PROJECT_DIR$/wqyblog_api_go.iml" />
|
<module fileurl="file://$PROJECT_DIR$/wqyblog_api_go.iml" filepath="$PROJECT_DIR$/wqyblog_api_go.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/wqyblog_webui.iml" filepath="$PROJECT_DIR$/wqyblog_webui.iml" />
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -0,0 +1,172 @@
|
||||||
|
# 博客 RESTful API 文档
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
|
||||||
|
本 API 提供了一个用于管理博客文章的接口,支持使用 Markdown 格式存储内容,并且支持分页查询。后端基于 Go 语言开发,支持 MySQL 和 PostgreSQL 数据库。
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- **Base URL**: `https://func-e-wiwafluoqc.cn-shanghai.fcapp.run/api`
|
||||||
|
- **Content-Type**: `application/json`
|
||||||
|
- **Authorization**: 使用 JWT 进行身份验证。在所有需要身份验证的请求中,必须在请求头中包含 `Authorization: Bearer {token}`。
|
||||||
|
|
||||||
|
## 测试认证密码
|
||||||
|
|
||||||
|
- **用户名**: `admin`
|
||||||
|
- **密码**: `123456@321`
|
||||||
|
|
||||||
|
## 认证
|
||||||
|
|
||||||
|
### 1. 用户登录
|
||||||
|
|
||||||
|
**描述**: 生成一个 JWT,用于后续的 API 调用。
|
||||||
|
|
||||||
|
- **URL**: `/auth/login`
|
||||||
|
- **方法**: `POST`
|
||||||
|
- **请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "admin",
|
||||||
|
"password": "your_secure_password"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"token": "jwt_token"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **说明**: 成功登录后,返回 JWT 令牌,用于后续请求的身份认证。
|
||||||
|
|
||||||
|
## 博文管理
|
||||||
|
|
||||||
|
### 2. 获取博文列表
|
||||||
|
|
||||||
|
**描述**: 获取博客文章列表,支持分页。
|
||||||
|
|
||||||
|
- **URL**: `/posts`
|
||||||
|
- **方法**: `GET`
|
||||||
|
- **请求参数**:
|
||||||
|
- `page` (可选): 页码,默认值为 `1`。
|
||||||
|
- `limit` (可选): 每页显示的记录数量,默认值为 `10`。
|
||||||
|
- **响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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..."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **说明**: 返回分页后的博文列表。
|
||||||
|
|
||||||
|
### 3. 获取单个博文
|
||||||
|
|
||||||
|
**描述**: 根据 `id` 获取指定的博客文章。
|
||||||
|
|
||||||
|
- **URL**: `/posts/:id`
|
||||||
|
- **方法**: `GET`
|
||||||
|
- **响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ID": 1,
|
||||||
|
"CreatedAt": "2024-08-20T12:00:00Z",
|
||||||
|
"UpdatedAt": "2024-08-20T12:00:00Z",
|
||||||
|
"DeletedAt": null,
|
||||||
|
"Title": "First Post",
|
||||||
|
"Content": "Markdown content here..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **说明**: 返回指定 `id` 的博文详情。如果该 `id` 对应的博文不存在,返回 `404` 错误。
|
||||||
|
|
||||||
|
### 4. 创建博文
|
||||||
|
|
||||||
|
**描述**: 创建一篇新的博客文章。
|
||||||
|
|
||||||
|
- **URL**: `/posts`
|
||||||
|
- **方法**: `POST`
|
||||||
|
- **请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "New Post Title",
|
||||||
|
"content": "Markdown content here..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ID": 3,
|
||||||
|
"CreatedAt": "2024-08-22T12:00:00Z",
|
||||||
|
"UpdatedAt": "2024-08-22T12:00:00Z",
|
||||||
|
"DeletedAt": null,
|
||||||
|
"Title": "New Post Title",
|
||||||
|
"Content": "Markdown content here..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **说明**: 创建一篇新的博文,并返回创建的博文信息。
|
||||||
|
|
||||||
|
### 5. 更新博文
|
||||||
|
|
||||||
|
**描述**: 更新指定 `id` 的博客文章。
|
||||||
|
|
||||||
|
- **URL**: `/posts/:id`
|
||||||
|
- **方法**: `PUT`
|
||||||
|
- **请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "Updated Post Title",
|
||||||
|
"content": "Updated markdown content..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ID": 1,
|
||||||
|
"CreatedAt": "2024-08-20T12:00:00Z",
|
||||||
|
"UpdatedAt": "2024-08-22T13:00:00Z",
|
||||||
|
"DeletedAt": null,
|
||||||
|
"Title": "Updated Post Title",
|
||||||
|
"Content": "Updated markdown content..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **说明**: 更新指定 `id` 的博文。如果该 `id` 对应的博文不存在,返回 `404` 错误。
|
||||||
|
|
||||||
|
### 6. 删除博文
|
||||||
|
|
||||||
|
**描述**: 删除指定 `id` 的博客文章。
|
||||||
|
|
||||||
|
- **URL**: `/posts/:id`
|
||||||
|
- **方法**: `DELETE`
|
||||||
|
- **响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "Post deleted successfully"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **说明**: 删除指定 `id` 的博文。如果该 `id` 对应的博文不存在,返回 `404` 错误。
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
API 在发生错误时会返回以下格式的错误响应:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "Error description"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **401 Unauthorized**: 需要有效的 JWT 令牌,但令牌未提供或无效。
|
||||||
|
- **404 Not Found**: 请求的资源不存在(如博文不存在)。
|
||||||
|
- **400 Bad Request**: 请求参数无效或缺失。
|
||||||
|
|
||||||
|
## 例外情况
|
||||||
|
|
||||||
|
在使用此 API 时,请确保使用 HTTPS 来保护数据传输的安全性。
|
|
@ -0,0 +1,68 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||||
|
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"wqyblog_api_go/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DB *gorm.DB
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
err := godotenv.Load()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Not found .env file. Only use system variables.")
|
||||||
|
}
|
||||||
|
|
||||||
|
dbType := os.Getenv("DB_TYPE")
|
||||||
|
dbUser := os.Getenv("DB_USER")
|
||||||
|
dbPassword := os.Getenv("DB_PASSWORD")
|
||||||
|
dbName := os.Getenv("DB_NAME")
|
||||||
|
dbHost := os.Getenv("DB_HOST")
|
||||||
|
dbPort := os.Getenv("DB_PORT")
|
||||||
|
|
||||||
|
var dbUri string
|
||||||
|
|
||||||
|
switch dbType {
|
||||||
|
case "mysql":
|
||||||
|
dbUri = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local",
|
||||||
|
dbUser, dbPassword, dbHost, dbPort, dbName)
|
||||||
|
case "postgresql":
|
||||||
|
dbUri = fmt.Sprintf("host=%s port=%s user=%s dbname=%s sslmode=disable password=%s",
|
||||||
|
dbHost, dbPort, dbUser, dbName, dbPassword)
|
||||||
|
default:
|
||||||
|
log.Fatalf("Unsupported DB_TYPE: %s", dbType)
|
||||||
|
}
|
||||||
|
|
||||||
|
DB, err = gorm.Open(dbType, dbUri)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to connect to database: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
DB.AutoMigrate(&models.Post{})
|
||||||
|
DB.AutoMigrate(&models.User{})
|
||||||
|
|
||||||
|
log.Println("Database connected")
|
||||||
|
|
||||||
|
// 如果没有ADMIN用户
|
||||||
|
if DB.First(&models.User{}, "username = ?", "admin").RecordNotFound() {
|
||||||
|
log.Println("No ADMIN user found, creating one...")
|
||||||
|
// 添加ADMIN用户 密码使用 bcrypt 123456@321
|
||||||
|
password, err := bcrypt.GenerateFromPassword([]byte("123456@321"), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to generate password: %v", err)
|
||||||
|
}
|
||||||
|
DB.Create(&models.User{
|
||||||
|
Username: "admin",
|
||||||
|
Password: password,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
log.Println("ADMIN user found, skipping creation...")
|
||||||
|
}
|
||||||
|
}
|
42
go.mod
42
go.mod
|
@ -1,3 +1,43 @@
|
||||||
module wqyblog_api_go
|
module wqyblog_api_go
|
||||||
|
|
||||||
go 1.19
|
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
|
||||||
|
golang.org/x/crypto v0.26.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
|
github.com/bytedance/sonic v1.12.1 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
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/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||||
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/lib/pq v1.10.9 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
|
golang.org/x/arch v0.9.0 // indirect
|
||||||
|
golang.org/x/net v0.28.0 // indirect
|
||||||
|
golang.org/x/sys v0.24.0 // indirect
|
||||||
|
golang.org/x/text v0.17.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||||
|
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
|
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
|
||||||
|
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
|
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
|
||||||
|
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||||
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
||||||
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
|
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||||
|
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
|
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-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=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
|
||||||
|
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||||
|
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
|
||||||
|
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||||
|
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||||
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||||
|
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||||
|
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
|
@ -0,0 +1,9 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
func HalloWorld(context *gin.Context) {
|
||||||
|
context.JSON(200, gin.H{
|
||||||
|
"message": "Hello World",
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/dgrijalva/jwt-go"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
"wqyblog_api_go/db"
|
||||||
|
"wqyblog_api_go/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoginInput struct {
|
||||||
|
Username string `json:"username" binding:"required"`
|
||||||
|
Password string `json:"password" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Login(c *gin.Context) {
|
||||||
|
var input LoginInput
|
||||||
|
if err := c.ShouldBindJSON(&input); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var user models.User
|
||||||
|
if err := db.DB.Where("username = ?", input.Username).First(&user).Error; err != nil {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid username or password"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证密码
|
||||||
|
if err := bcrypt.CompareHashAndPassword(user.Password, []byte(input.Password)); err != nil {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid username or password"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成 JWT 令牌
|
||||||
|
token, err := generateToken(user.Username)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"token": token})
|
||||||
|
}
|
||||||
|
|
||||||
|
var jwtKey = []byte(os.Getenv("JWT_SECRET"))
|
||||||
|
|
||||||
|
type Claims struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
jwt.StandardClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateToken(username string) (string, error) {
|
||||||
|
expirationTime := time.Now().Add(1 * time.Hour)
|
||||||
|
claims := &Claims{
|
||||||
|
Username: username,
|
||||||
|
StandardClaims: jwt.StandardClaims{
|
||||||
|
ExpiresAt: expirationTime.Unix(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
tokenString, err := token.SignedString(jwtKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenString, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Authenticate(c *gin.Context) {
|
||||||
|
tokenString := c.GetHeader("Authorization")
|
||||||
|
if tokenString == "" {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Request does not contain an access token"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove "Bearer " prefix if present
|
||||||
|
if len(tokenString) > 7 && tokenString[:7] == "Bearer " {
|
||||||
|
tokenString = tokenString[7:]
|
||||||
|
}
|
||||||
|
|
||||||
|
claims := &Claims{}
|
||||||
|
|
||||||
|
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
return jwtKey, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, jwt.ErrSignatureInvalid) {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token signature"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ve, ok := err.(*jwt.ValidationError); ok && ve.Errors&jwt.ValidationErrorExpired != 0 {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token is expired"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Bad request"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !token.Valid {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally, you can check the token's claims
|
||||||
|
c.Set("username", claims.Username)
|
||||||
|
c.Next()
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"wqyblog_api_go/db"
|
||||||
|
"wqyblog_api_go/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPosts(c *gin.Context) {
|
||||||
|
// 获取查询参数
|
||||||
|
pageStr := c.DefaultQuery("page", "1")
|
||||||
|
limitStr := c.DefaultQuery("limit", "10")
|
||||||
|
|
||||||
|
// 将查询参数转换为整数
|
||||||
|
page, err := strconv.Atoi(pageStr)
|
||||||
|
if err != nil || page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
limit, err := strconv.Atoi(limitStr)
|
||||||
|
if err != nil || limit < 1 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算偏移量
|
||||||
|
offset := (page - 1) * limit
|
||||||
|
|
||||||
|
var posts []models.Post
|
||||||
|
db.DB.Limit(limit).Offset(offset).Find(&posts)
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"page": page,
|
||||||
|
"limit": limit,
|
||||||
|
"data": posts,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPost(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
var post models.Post
|
||||||
|
if err := db.DB.First(&post, id).Error; err != nil {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "Post not found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, post)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreatePost(c *gin.Context) {
|
||||||
|
var input models.Post
|
||||||
|
if err := c.ShouldBindJSON(&input); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
post := models.Post{Title: input.Title, Content: input.Content}
|
||||||
|
db.DB.Create(&post)
|
||||||
|
c.JSON(http.StatusOK, post)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdatePost(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
var post models.Post
|
||||||
|
if err := db.DB.First(&post, id).Error; err != nil {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "Post not found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var input models.Post
|
||||||
|
if err := c.ShouldBindJSON(&input); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
db.DB.Model(&post).Updates(models.Post{Title: input.Title, Content: input.Content})
|
||||||
|
c.JSON(http.StatusOK, post)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeletePost(c *gin.Context) {
|
||||||
|
id := c.Param("id")
|
||||||
|
var post models.Post
|
||||||
|
if err := db.DB.First(&post, id).Error; err != nil {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "Post not found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
db.DB.Delete(&post)
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "Post deleted successfully"})
|
||||||
|
}
|
30
main.go
30
main.go
|
@ -1,7 +1,33 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"wqyblog_api_go/db"
|
||||||
|
"wqyblog_api_go/handlers"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("Hello, World!")
|
db.Init()
|
||||||
|
|
||||||
|
r := gin.Default()
|
||||||
|
|
||||||
|
r.GET("/", handlers.HalloWorld)
|
||||||
|
|
||||||
|
r.POST("/api/auth/login", handlers.Login)
|
||||||
|
|
||||||
|
r.GET("/api/posts", handlers.GetPosts)
|
||||||
|
r.GET("/api/posts/:id", handlers.GetPost)
|
||||||
|
|
||||||
|
authorized := r.Group("/")
|
||||||
|
authorized.Use(handlers.Authenticate)
|
||||||
|
{
|
||||||
|
authorized.POST("/api/posts", handlers.CreatePost)
|
||||||
|
authorized.PUT("/api/posts/:id", handlers.UpdatePost)
|
||||||
|
authorized.DELETE("/api/posts/:id", handlers.DeletePost)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.Run(":8080")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Post struct {
|
||||||
|
gorm.Model
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
gorm.Model
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password []byte `json:"password"`
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
POST http://localhost:8080/api/auth/login
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"username": "admin",
|
||||||
|
"password": "123456@321"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
POST http://localhost:8080/api/posts
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzI0MTMxNjIxfQ.NZu5hhl7XjT9BdjIPmBAknekNHIavZYh5Xzz39PUzkk
|
||||||
|
|
||||||
|
{
|
||||||
|
"title": "New Post Title",
|
||||||
|
"content": "Markdown content here..."
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
GET http://localhost:8080/api/posts
|
||||||
|
Content-Type: application/json
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$/wqyblog_webui" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -0,0 +1,11 @@
|
||||||
|
# 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
|
|
@ -0,0 +1,194 @@
|
||||||
|
## 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
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Include your project-specific ignores in this file
|
||||||
|
# Read about how to use .gitignore: https://help.github.com/articles/ignoring-files
|
||||||
|
# Useful .gitignore templates: https://github.com/github/gitignore
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.cache
|
|
@ -0,0 +1,62 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>找不到页面</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
color: #888;
|
||||||
|
display: table;
|
||||||
|
font-family: sans-serif;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 2em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #555;
|
||||||
|
font-size: 2em;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 280px) {
|
||||||
|
|
||||||
|
body,
|
||||||
|
p {
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin: 0 0 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>找不到页面</h1>
|
||||||
|
<p>抱歉,您尝试查看的页面不存在。</p>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
<!-- IE needs 512+ bytes: https://docs.microsoft.com/archive/blogs/ieinternals/friendly-http-error-pages -->
|
|
@ -0,0 +1,19 @@
|
||||||
|
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.
|
|
@ -0,0 +1,220 @@
|
||||||
|
|
||||||
|
html {
|
||||||
|
color: #222;
|
||||||
|
font-size: 1em;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-selection {
|
||||||
|
background: #b3d4fc;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: #b3d4fc;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 更好看的默认水平线
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
display: block;
|
||||||
|
height: 1px;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
margin: 1em 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 删除音频、画布、iframe 之间的间隙,
|
||||||
|
* 图像、视频及其容器底部:
|
||||||
|
* https://github.com/h5bp/html5-boilerplate/issues/440
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio,
|
||||||
|
canvas,
|
||||||
|
iframe,
|
||||||
|
img,
|
||||||
|
svg,
|
||||||
|
video {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 删除默认字段集样式。
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 仅允许垂直调整文本区域的大小。
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
自定义样式
|
||||||
|
========================================================================== */
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
width: 100vw;
|
||||||
|
box-shadow: 0 0 1em rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1em;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
辅助类
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.hidden,
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visually-hidden {
|
||||||
|
border: 0;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 1px;
|
||||||
|
/* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.visually-hidden.focusable:active,
|
||||||
|
.visually-hidden.focusable:focus {
|
||||||
|
clip: auto;
|
||||||
|
height: auto;
|
||||||
|
margin: 0;
|
||||||
|
overflow: visible;
|
||||||
|
position: static;
|
||||||
|
white-space: inherit;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.invisible {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearfix::before,
|
||||||
|
.clearfix::after {
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearfix::after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (min-width: 35em) {
|
||||||
|
/* 对满足条件的视口进行样式调整 */
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print,
|
||||||
|
(-webkit-min-device-pixel-ratio: 1.25),
|
||||||
|
(min-resolution: 1.25dppx),
|
||||||
|
(min-resolution: 120dpi) {
|
||||||
|
/* 针对高分辨率设备的风格调整 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
打印样式。
|
||||||
|
内联以避免额外的 HTTP 请求:
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
background: #fff !important;
|
||||||
|
color: #000 !important;
|
||||||
|
/* Black prints faster */
|
||||||
|
box-shadow: none !important;
|
||||||
|
text-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
a:visited {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
a[href]::after {
|
||||||
|
content: " (" attr(href) ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title]::after {
|
||||||
|
content: " (" attr(title) ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't show links that are fragment identifiers,
|
||||||
|
* or use the `javascript:` pseudo protocol
|
||||||
|
*/
|
||||||
|
a[href^="#"]::after,
|
||||||
|
a[href^="javascript:"]::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
white-space: pre-wrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
blockquote {
|
||||||
|
border: 1px solid #999;
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr,
|
||||||
|
img {
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
orphans: 3;
|
||||||
|
widows: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
page-break-after: avoid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 766 B |
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 192 192"><path fill="#e08524" d="M75.3 73.4H18.4l45.3 34.3L48.3 163l46.1-32.3 48.2 34.6-16.9-58.3 44.9-33.6H115l-20.5-55-19.2 55z"/><path d="m96.7 18.8 18.2 8.2 16.5 44.3h-15.1L96.7 18.8zm-47 146 18.7 9.9 42.6-29.9-16.5-11.4-44.8 31.4zm79.1-56.8 17.4 9.4 18.6 60.1-19.7-11.3-16.3-58.2z"/><path d="m173.1 74.3 17.8 9.2-44.7 34-17.4-9.4 44.3-33.8z"/></svg>
|
After Width: | Height: | Size: 429 B |
|
@ -0,0 +1,49 @@
|
||||||
|
<!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">
|
||||||
|
<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">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<div class="header-title">
|
||||||
|
WqyBlog.cn
|
||||||
|
</div>
|
||||||
|
<div class="login">
|
||||||
|
<a href="login.html">登录</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="main">
|
||||||
|
<div class="main-content">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="main-sidebar">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="jquery/dist/jquery.js"></script>
|
||||||
|
<script src="./js/app.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1 @@
|
||||||
|
// app.js wqyblog.cn
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<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">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
# www.robotstxt.org/
|
||||||
|
|
||||||
|
# Allow crawling of all content
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"short_name": "",
|
||||||
|
"name": "",
|
||||||
|
"icons": [{
|
||||||
|
"src": "icon.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
}],
|
||||||
|
"start_url": "/?utm_source=homescreen",
|
||||||
|
"background_color": "#fafafa",
|
||||||
|
"theme_color": "#fafafa"
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: {
|
||||||
|
app: './js/app.js',
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
clean: true,
|
||||||
|
filename: './js/app.js',
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,13 @@
|
||||||
|
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: ['./'],
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
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