mirror of
https://github.com/snowykami/neo-blog.git
synced 2025-09-05 16:56:22 +00:00
⚡️ feat: add main page layout with navigation and footer
feat: create random labels page feat: implement login page with OpenID Connect support feat: add Gravatar component for user avatars feat: create Navbar component with navigation menu chore: create Sidebar component placeholder feat: implement login form with OIDC and email/password options feat: add reusable button component feat: create card component for structured content display feat: implement input component for forms feat: create label component for form labels feat: add navigation menu component for site navigation chore: add configuration file for site metadata feat: implement device context for responsive design feat: add utility functions for class name management feat: define OIDC configuration model feat: define base response model for API responses feat: define user model for user data feat: implement i18n for internationalization support feat: add English and Chinese translations for login chore: create index for locale resources chore: add blog home view placeholder
This commit is contained in:
@ -1 +1,9 @@
|
||||
package model
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type Category struct {
|
||||
gorm.Model
|
||||
Name string `gorm:"type:text;not null"`
|
||||
Description string `gorm:"type:text;not null"` // 分类描述
|
||||
}
|
||||
|
@ -1,11 +1,24 @@
|
||||
package model
|
||||
|
||||
import "gorm.io/gorm"
|
||||
import (
|
||||
"github.com/snowykami/neo-blog/internal/dto"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Label struct {
|
||||
gorm.Model
|
||||
Key string `gorm:"uniqueIndex"` // 标签键,唯一标识
|
||||
Value string `gorm:"type:text"` // 标签值,描述标签的内容
|
||||
Color string `gorm:"type:text"` // 前端可用颜色代码
|
||||
TailwindClassName string `gorm:"type:text"` // Tailwind CSS 的类名,用于前端样式
|
||||
Key string `gorm:"uniqueIndex"` // 标签键,唯一标识
|
||||
Value string `gorm:"type:text"` // 标签值,描述标签的内容
|
||||
Color string `gorm:"type:text"` // 前端可用颜色代码
|
||||
TailwindClassName string `gorm:"type:text"` // Tailwind CSS 的类名,用于前端样式
|
||||
Posts []Post `gorm:"many2many:post_labels;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` // 关联的帖子
|
||||
}
|
||||
|
||||
func (l *Label) ToDto() dto.LabelDto {
|
||||
return dto.LabelDto{
|
||||
Key: l.Key,
|
||||
Value: l.Value,
|
||||
Color: l.Color,
|
||||
TailwindClassName: l.TailwindClassName,
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/snowykami/neo-blog/internal/dto"
|
||||
"gorm.io/gorm"
|
||||
"resty.dev/v3"
|
||||
@ -17,6 +18,7 @@ type OidcConfig struct {
|
||||
Icon string // 图标url,为空则使用内置默认图标
|
||||
OidcDiscoveryUrl string // OpenID自动发现URL,例如 :https://pass.liteyuki.icu/.well-known/openid-configuration
|
||||
Enabled bool `gorm:"default:true"` // 是否启用
|
||||
Type string `gorm:"oauth2"` // OIDC类型,默认为oauth2,也可以为misskey
|
||||
// 以下字段为自动获取字段,每次更新配置时自动填充
|
||||
Issuer string
|
||||
AuthorizationEndpoint string
|
||||
@ -42,7 +44,7 @@ type oidcDiscoveryResp struct {
|
||||
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
|
||||
}
|
||||
|
||||
func updateOidcConfigFromUrl(url string) (*oidcDiscoveryResp, error) {
|
||||
func updateOidcConfigFromUrl(url string, typ string) (*oidcDiscoveryResp, error) {
|
||||
client := resty.New()
|
||||
client.SetTimeout(10 * time.Second) // 设置超时时间
|
||||
var discovery oidcDiscoveryResp
|
||||
@ -57,6 +59,11 @@ func updateOidcConfigFromUrl(url string) (*oidcDiscoveryResp, error) {
|
||||
return nil, fmt.Errorf("请求OIDC发现端点失败,状态码: %d", resp.StatusCode())
|
||||
}
|
||||
// 验证必要字段
|
||||
if typ == "misskey" {
|
||||
discovery.UserInfoEndpoint = discovery.Issuer + "/api/users/me" // Misskey的用户信息端点
|
||||
discovery.JwksUri = discovery.Issuer + "/api/jwks"
|
||||
}
|
||||
fmt.Println(discovery)
|
||||
if discovery.Issuer == "" ||
|
||||
discovery.AuthorizationEndpoint == "" ||
|
||||
discovery.TokenEndpoint == "" ||
|
||||
@ -69,10 +76,12 @@ func updateOidcConfigFromUrl(url string) (*oidcDiscoveryResp, error) {
|
||||
|
||||
func (o *OidcConfig) BeforeSave(tx *gorm.DB) (err error) {
|
||||
// 只有在创建新记录或更新 OidcDiscoveryUrl 字段时才更新端点信息
|
||||
if tx.Statement.Changed("OidcDiscoveryUrl") {
|
||||
discoveryResp, err := updateOidcConfigFromUrl(o.OidcDiscoveryUrl)
|
||||
if tx.Statement.Changed("OidcDiscoveryUrl") || o.ID == 0 {
|
||||
logrus.Infof("Updating OIDC config for %s, OidcDiscoveryUrl: %s", o.Name, o.OidcDiscoveryUrl)
|
||||
discoveryResp, err := updateOidcConfigFromUrl(o.OidcDiscoveryUrl, o.Type)
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新OIDC配置失败: %w", err)
|
||||
logrus.Error("Updating OIDC config failed: ", err)
|
||||
return fmt.Errorf("updating OIDC config failed: %w", err)
|
||||
}
|
||||
o.Issuer = discoveryResp.Issuer
|
||||
o.AuthorizationEndpoint = discoveryResp.AuthorizationEndpoint
|
||||
|
@ -1,16 +1,57 @@
|
||||
package model
|
||||
|
||||
import "gorm.io/gorm"
|
||||
import (
|
||||
"github.com/snowykami/neo-blog/internal/dto"
|
||||
"github.com/snowykami/neo-blog/pkg/constant"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Post struct {
|
||||
gorm.Model
|
||||
UserID uint `gorm:"index"` // 发布者的用户ID
|
||||
User User `gorm:"foreignKey:UserID;references:ID"` // 关联的用户
|
||||
Title string `gorm:"type:text;not null"` // 帖子标题
|
||||
Content string `gorm:"type:text;not null"` // 帖子内容
|
||||
Labels []Label `gorm:"many2many:post_labels;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` // 关联的标签
|
||||
IsPrivate bool `gorm:"default:false"` // 是否为私密帖子
|
||||
UserID uint `gorm:"index"` // 发布者的用户ID
|
||||
User User `gorm:"foreignKey:UserID;references:ID"` // 关联的用户
|
||||
Title string `gorm:"type:text;not null"` // 帖子标题
|
||||
Content string `gorm:"type:text;not null"` // 帖子内容
|
||||
CategoryID uint `gorm:"index"` // 帖子分类ID
|
||||
Category Category `gorm:"foreignKey:CategoryID;references:ID"` // 关联的分类
|
||||
Labels []Label `gorm:"many2many:post_labels;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` // 关联的标签
|
||||
IsPrivate bool `gorm:"default:false"` // 是否为私密帖子
|
||||
LikeCount uint64
|
||||
CommentCount uint64
|
||||
VisitorCount uint64
|
||||
ViewCount uint64
|
||||
Heat uint64 `gorm:"default:0"`
|
||||
}
|
||||
|
||||
// CalculateHeat 热度计算
|
||||
func (p *Post) CalculateHeat() float64 {
|
||||
return float64(
|
||||
p.LikeCount*constant.HeatFactorLikeWeight +
|
||||
p.CommentCount*constant.HeatFactorCommentWeight +
|
||||
p.ViewCount*constant.HeatFactorViewWeight,
|
||||
)
|
||||
}
|
||||
|
||||
// AfterUpdate 热度指标更新后更新热度
|
||||
func (p *Post) AfterUpdate(tx *gorm.DB) (err error) {
|
||||
if tx.Statement.Changed("LikeCount") || tx.Statement.Changed("CommentCount") || tx.Statement.Changed("ViewCount") {
|
||||
p.Heat = uint64(p.CalculateHeat())
|
||||
if err := tx.Model(p).Update("heat", p.Heat).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Post) ToDto() dto.PostDto {
|
||||
return dto.PostDto{
|
||||
ID: p.ID,
|
||||
UserID: p.UserID,
|
||||
Title: p.Title,
|
||||
Content: p.Content,
|
||||
IsPrivate: p.IsPrivate,
|
||||
LikeCount: p.LikeCount,
|
||||
CommentCount: p.CommentCount,
|
||||
ViewCount: p.ViewCount,
|
||||
Heat: p.Heat,
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user