Refactor comment components and update OIDC configuration

- Updated OIDC configuration to include additional fields in the UpdateOidcConfig method.
- Enhanced CommentService to include IsPrivate field in the comment DTO.
- Refactored comment components: renamed neo-comment to comment, and moved related files.
- Implemented new CommentInput and CommentItem components for better structure and readability.
- Removed obsolete files related to the old comment system.
- Added CSS animations for comment components to improve user experience.
This commit is contained in:
2025-09-09 22:37:27 +08:00
parent 5fb6cd63dd
commit cb3f602663
13 changed files with 190 additions and 179 deletions

View File

@ -1,116 +1,117 @@
package model
import (
"fmt"
"github.com/sirupsen/logrus"
"github.com/snowykami/neo-blog/internal/dto"
"gorm.io/gorm"
"resty.dev/v3"
"time"
"fmt"
"time"
"github.com/sirupsen/logrus"
"github.com/snowykami/neo-blog/internal/dto"
"gorm.io/gorm"
"resty.dev/v3"
)
type OidcConfig struct {
gorm.Model
Name string `gorm:"uniqueIndex"` // OIDC配置名称唯一
ClientID string // 客户端ID
ClientSecret string // 客户端密钥
DisplayName string // 显示名称,例如:轻雪通行证
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
TokenEndpoint string
UserInfoEndpoint string
JwksUri string
gorm.Model
Name string `gorm:"uniqueIndex"` // OIDC配置名称唯一
ClientID string // 客户端ID
ClientSecret string // 客户端密钥
DisplayName string // 显示名称,例如:轻雪通行证
Icon string // 图标url为空则使用内置默认图标
OidcDiscoveryUrl string // OpenID自动发现URL例如 https://pass.liteyuki.org/.well-known/openid-configuration
Enabled bool `gorm:"default:true"` // 是否启用
Type string `gorm:"oauth2"` // OIDC类型默认为oauth2,也可以为misskey
// 以下字段为自动获取字段,每次更新配置时自动填充
Issuer string
AuthorizationEndpoint string
TokenEndpoint string
UserInfoEndpoint string
JwksUri string
}
type oidcDiscoveryResp struct {
Issuer string `json:"issuer" validate:"required"`
AuthorizationEndpoint string `json:"authorization_endpoint" validate:"required"`
TokenEndpoint string `json:"token_endpoint" validate:"required"`
UserInfoEndpoint string `json:"userinfo_endpoint" validate:"required"`
JwksUri string `json:"jwks_uri" validate:"required"`
// 可选字段
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
IdTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
ClaimsSupported []string `json:"claims_supported,omitempty"`
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
Issuer string `json:"issuer" validate:"required"`
AuthorizationEndpoint string `json:"authorization_endpoint" validate:"required"`
TokenEndpoint string `json:"token_endpoint" validate:"required"`
UserInfoEndpoint string `json:"userinfo_endpoint" validate:"required"`
JwksUri string `json:"jwks_uri" validate:"required"`
// 可选字段
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
IdTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
ClaimsSupported []string `json:"claims_supported,omitempty"`
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
}
func updateOidcConfigFromUrl(url string, typ string) (*oidcDiscoveryResp, error) {
client := resty.New()
client.SetTimeout(10 * time.Second) // 设置超时时间
var discovery oidcDiscoveryResp
resp, err := client.R().
SetHeader("Accept", "application/json").
SetResult(&discovery).
Get(url)
if err != nil {
return nil, fmt.Errorf("请求OIDC发现端点失败: %w", err)
}
if resp.StatusCode() != 200 {
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 == "" ||
discovery.UserInfoEndpoint == "" ||
discovery.JwksUri == "" {
return nil, fmt.Errorf("OIDC发现端点响应缺少必要字段")
}
return &discovery, nil
client := resty.New()
client.SetTimeout(10 * time.Second) // 设置超时时间
var discovery oidcDiscoveryResp
resp, err := client.R().
SetHeader("Accept", "application/json").
SetResult(&discovery).
Get(url)
if err != nil {
return nil, fmt.Errorf("请求OIDC发现端点失败: %w", err)
}
if resp.StatusCode() != 200 {
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 == "" ||
discovery.UserInfoEndpoint == "" ||
discovery.JwksUri == "" {
return nil, fmt.Errorf("OIDC发现端点响应缺少必要字段")
}
return &discovery, nil
}
func (o *OidcConfig) BeforeSave(tx *gorm.DB) (err error) {
// 只有在创建新记录或更新 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 {
logrus.Error("Updating OIDC config failed: ", err)
return fmt.Errorf("updating OIDC config failed: %w", err)
}
o.Issuer = discoveryResp.Issuer
o.AuthorizationEndpoint = discoveryResp.AuthorizationEndpoint
o.TokenEndpoint = discoveryResp.TokenEndpoint
o.UserInfoEndpoint = discoveryResp.UserInfoEndpoint
o.JwksUri = discoveryResp.JwksUri
}
return nil
// 只有在创建新记录或更新 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 {
logrus.Error("Updating OIDC config failed: ", err)
return fmt.Errorf("updating OIDC config failed: %w", err)
}
o.Issuer = discoveryResp.Issuer
o.AuthorizationEndpoint = discoveryResp.AuthorizationEndpoint
o.TokenEndpoint = discoveryResp.TokenEndpoint
o.UserInfoEndpoint = discoveryResp.UserInfoEndpoint
o.JwksUri = discoveryResp.JwksUri
}
return nil
}
// ToUserDto 返回给用户侧
func (o *OidcConfig) ToUserDto() *dto.UserOidcConfigDto {
return &dto.UserOidcConfigDto{
Name: o.Name,
DisplayName: o.DisplayName,
Icon: o.Icon,
}
return &dto.UserOidcConfigDto{
Name: o.Name,
DisplayName: o.DisplayName,
Icon: o.Icon,
}
}
// ToAdminDto 返回给管理员侧
func (o *OidcConfig) ToAdminDto() *dto.AdminOidcConfigDto {
return &dto.AdminOidcConfigDto{
ID: o.ID,
Name: o.Name,
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
DisplayName: o.DisplayName,
Icon: o.Icon,
OidcDiscoveryUrl: o.OidcDiscoveryUrl,
Enabled: o.Enabled,
}
return &dto.AdminOidcConfigDto{
ID: o.ID,
Name: o.Name,
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
DisplayName: o.DisplayName,
Icon: o.Icon,
OidcDiscoveryUrl: o.OidcDiscoveryUrl,
Enabled: o.Enabled,
}
}