Merge pull request #2 from fthvgb1/dev

Dev
This commit is contained in:
2023-01-23 23:45:35 +08:00 committed by GitHub
commit f470a0c31f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 548 additions and 58 deletions

View File

@ -71,3 +71,7 @@ trustIps: []
trustServerNames: ["xy.test","blog.xy.test"]
# port
port: 8082
# 主题 为空值为option template没有就默认为twentyfifteen
theme: "twentyfifteen"
# 文档排序默认升序还是降序
postOrder: "desc"

1
helper/html/a.gohtml Normal file
View File

@ -0,0 +1 @@
{{.xx}}

View File

@ -1,9 +1,11 @@
package html
import (
"bytes"
"fmt"
"github.com/dlclark/regexp2"
"github.com/fthvgb1/wp-go/helper/slice"
"html/template"
"regexp"
"strings"
)
@ -164,3 +166,13 @@ func UnClosedTag(s []string) []string {
i++
}
}
func RenderedHtml(t *template.Template, data map[string]any) (r string, err error) {
var buf bytes.Buffer
err = t.Execute(&buf, data)
if err != nil {
return
}
r = buf.String()
return
}

View File

@ -1,6 +1,7 @@
package html
import (
"html/template"
"reflect"
"testing"
)
@ -228,3 +229,46 @@ func Test_clearTag(t *testing.T) {
})
}
}
func TestRenderedHtml(t *testing.T) {
type args struct {
t *template.Template
data map[string]any
}
tests := []struct {
name string
args args
wantR string
wantErr bool
}{
{
name: "t1",
args: args{
t: func() *template.Template {
tt, err := template.ParseFiles("./a.gohtml")
if err != nil {
panic(err)
}
return tt
}(),
data: map[string]any{
"xx": "oo",
},
},
wantR: "oo",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotR, err := RenderedHtml(tt.args.t, tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("RenderedHtml() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotR != tt.wantR {
t.Errorf("RenderedHtml() gotR = %v, want %v", gotR, tt.wantR)
}
})
}
}

114
helper/slice/set.go Normal file
View File

@ -0,0 +1,114 @@
package slice
func IsContained[T comparable](a T, arr []T) bool {
for _, v := range arr {
if a == v {
return true
}
}
return false
}
func IsContainedByFn[T any](a []T, e T, fn func(i, j T) bool) bool {
for _, t := range a {
if fn(e, t) {
return true
}
}
return false
}
// Diff return elements which in a and not in b,...
func Diff[T comparable](a []T, b ...[]T) (r []T) {
for _, t := range a {
f := false
for _, ts := range b {
if IsContained(t, ts) {
f = true
break
}
}
if f {
continue
}
r = append(r, t)
}
return
}
func DiffByFn[T any](a []T, fn func(i, j T) bool, b ...[]T) (r []T) {
for _, t := range a {
f := false
for _, ts := range b {
if IsContainedByFn(ts, t, fn) {
f = true
break
}
}
if f {
continue
}
r = append(r, t)
}
return
}
func Intersect[T comparable](a []T, b ...[]T) (r []T) {
for _, t := range a {
f := false
for _, ts := range b {
if !IsContained(t, ts) {
f = true
break
}
}
if f {
continue
}
r = append(r, t)
}
return
}
func IntersectByFn[T any](a []T, fn func(i, j T) bool, b ...[]T) (r []T) {
for _, t := range a {
f := false
for _, ts := range b {
if !IsContainedByFn(ts, t, fn) {
f = true
break
}
}
if f {
continue
}
r = append(r, t)
}
return
}
func Unique[T comparable](a ...[]T) (r []T) {
m := map[T]struct{}{}
for _, ts := range a {
for _, t := range ts {
if _, ok := m[t]; !ok {
m[t] = struct{}{}
r = append(r, t)
} else {
continue
}
}
}
return
}
func UniqueByFn[T any](fn func(T, T) bool, a ...[]T) (r []T) {
for _, ts := range a {
for _, t := range ts {
if !IsContainedByFn(r, t, fn) {
r = append(r, t)
}
}
}
return r
}

211
helper/slice/set_test.go Normal file
View File

@ -0,0 +1,211 @@
package slice
import (
"github.com/fthvgb1/wp-go/helper/number"
"reflect"
"testing"
)
type x struct {
int
y string
}
func y(i []int) []x {
return Map(i, func(t int) x {
return x{t, ""}
})
}
func TestDiff(t *testing.T) {
type args[T int] struct {
a []T
b [][]T
}
type testCase[T int] struct {
name string
args args[T]
wantR []T
}
tests := []testCase[int]{
{
name: "t1",
args: args[int]{
a: number.Range(1, 10, 1),
b: [][]int{number.Range(3, 7, 1), number.Range(6, 9, 1)},
},
wantR: []int{1, 2, 10},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotR := Diff(tt.args.a, tt.args.b...); !reflect.DeepEqual(gotR, tt.wantR) {
t.Errorf("Diff() = %v, want %v", gotR, tt.wantR)
}
})
}
}
func TestDiffByFn(t *testing.T) {
type args[T x] struct {
a []T
fn func(i, j T) bool
b [][]T
}
type testCase[T x] struct {
name string
args args[T]
wantR []T
}
tests := []testCase[x]{
{
name: "t1",
args: args[x]{
a: y(number.Range(1, 10, 1)),
fn: func(i, j x) bool {
return i.int == j.int
},
b: [][]x{
y(number.Range(3, 7, 1)),
y(number.Range(6, 9, 1)),
},
},
wantR: []x{{1, ""}, {2, ""}, {10, ""}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotR := DiffByFn(tt.args.a, tt.args.fn, tt.args.b...); !reflect.DeepEqual(gotR, tt.wantR) {
t.Errorf("DiffByFn() = %v, want %v", gotR, tt.wantR)
}
})
}
}
func TestIntersect(t *testing.T) {
type args[T int] struct {
a []T
b [][]T
}
type testCase[T int] struct {
name string
args args[T]
wantR []T
}
tests := []testCase[int]{
{
name: "t1",
args: args[int]{
a: number.Range(1, 10, 1),
b: [][]int{number.Range(3, 7, 1), number.Range(6, 9, 1)},
},
wantR: []int{6, 7},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotR := Intersect(tt.args.a, tt.args.b...); !reflect.DeepEqual(gotR, tt.wantR) {
t.Errorf("Intersect() = %v, want %v", gotR, tt.wantR)
}
})
}
}
func TestIntersectByFn(t *testing.T) {
type args[T x] struct {
a []T
fn func(i, j T) bool
b [][]T
}
type testCase[T x] struct {
name string
args args[T]
wantR []T
}
tests := []testCase[x]{
{
name: "t1",
args: args[x]{
a: y(number.Range(1, 10, 1)),
fn: func(i, j x) bool {
return i.int == j.int
},
b: [][]x{
y(number.Range(3, 7, 1)),
y(number.Range(6, 9, 1)),
},
},
wantR: []x{{6, ""}, {7, ""}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotR := IntersectByFn(tt.args.a, tt.args.fn, tt.args.b...); !reflect.DeepEqual(gotR, tt.wantR) {
t.Errorf("IntersectByFn() = %v, want %v", gotR, tt.wantR)
}
})
}
}
func TestUnique(t *testing.T) {
type args[T int] struct {
a [][]T
}
type testCase[T int] struct {
name string
args args[T]
wantR []T
}
tests := []testCase[int]{
{
name: "t1",
args: args[int]{
a: [][]int{
number.Range(1, 5, 1),
number.Range(3, 6, 1),
number.Range(6, 15, 1),
},
},
wantR: number.Range(1, 15, 1),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotR := Unique(tt.args.a...); !reflect.DeepEqual(gotR, tt.wantR) {
t.Errorf("Unique() = %v, want %v", gotR, tt.wantR)
}
})
}
}
func TestUniqueByFn(t *testing.T) {
type args[T x] struct {
fn func(T, T) bool
a [][]T
}
type testCase[T x] struct {
name string
args args[T]
wantR []T
}
tests := []testCase[x]{
{
name: "t1",
args: args[x]{
fn: func(i, j x) bool {
return i.int == j.int
},
a: [][]x{y([]int{1, 1, 2, 2, 3, 3}), y([]int{2, 2, 4, 4})},
},
wantR: y([]int{1, 2, 3, 4}),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotR := UniqueByFn(tt.args.fn, tt.args.a...); !reflect.DeepEqual(gotR, tt.wantR) {
t.Errorf("UniqueByFn() = %v, want %v", gotR, tt.wantR)
}
})
}
}

View File

@ -1,5 +1,7 @@
package slice
import "github.com/fthvgb1/wp-go/helper"
func Map[T, R any](arr []T, fn func(T) R) []R {
r := make([]R, 0, len(arr))
for _, t := range arr {
@ -146,11 +148,19 @@ func Comb[T any](arr []T, m int) (r [][]T) {
return r
}
func IsContained[T comparable](a T, arr []T) bool {
for _, v := range arr {
if a == v {
return true
func GroupBy[K comparable, T, V any](a []T, fn func(T) (K, V)) map[K][]V {
r := make(map[K][]V)
for _, t := range a {
k, v := fn(t)
if _, ok := r[k]; !ok {
r[k] = []V{v}
} else {
r[k] = append(r[k], v)
}
}
return false
return r
}
func ToAnySlice[T any](a []T) []any {
return Map(a, helper.ToAny[T])
}

View File

@ -463,3 +463,67 @@ func TestFilterAndMap(t *testing.T) {
})
}
}
func TestGroupBy(t *testing.T) {
type args[T x, K int, V string] struct {
a []T
fn func(T) (K, V)
}
type testCase[T x, K int, V string] struct {
name string
args args[T, K, V]
want map[K][]V
}
tests := []testCase[x, int, string]{
{
name: "t1",
args: args[x, int, string]{
a: Map([]int{1, 1, 2, 2, 3, 3, 4, 4, 5, 5}, func(t int) x {
return x{t, number.ToString(t)}
}),
fn: func(v x) (int, string) {
return v.int, v.y
},
},
want: map[int][]string{
1: {"1", "1"},
2: {"2", "2"},
3: {"3", "3"},
4: {"4", "4"},
5: {"5", "5"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GroupBy(tt.args.a, tt.args.fn); !reflect.DeepEqual(got, tt.want) {
t.Errorf("GroupBy() = %v, want %v", got, tt.want)
}
})
}
}
func TestToAnySlice(t *testing.T) {
type args[T int] struct {
a []T
}
type testCase[T int] struct {
name string
args args[T]
want []any
}
tests := []testCase[int]{
{
name: "t1",
args: args[int]{number.Range(1, 5, 1)},
want: []any{1, 2, 3, 4, 5},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ToAnySlice(tt.args.a); !reflect.DeepEqual(got, tt.want) {
t.Errorf("ToAnySlice() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -2,7 +2,6 @@ package actions
import (
"fmt"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/internal/pkg/cache"
"github.com/fthvgb1/wp-go/internal/pkg/logs"
"github.com/fthvgb1/wp-go/internal/pkg/models"
@ -30,7 +29,7 @@ func Detail(c *gin.Context) {
archive := cache.Archives(c)
categoryItems := cache.Categories(c)
recentComments := cache.RecentComments(c, 5)
var h = gin.H{
var ginH = gin.H{
"title": wpconfig.Options.Value("blogname"),
"options": wpconfig.Options,
"recentPosts": recent,
@ -39,17 +38,21 @@ func Detail(c *gin.Context) {
"recentComments": recentComments,
}
isApproveComment := false
status := plugins.Ok
defer func() {
status := http.StatusOK
code := http.StatusOK
if err != nil {
status = http.StatusInternalServerError
code = http.StatusNotFound
c.Error(err)
status = plugins.Error
return
}
if isApproveComment == true {
return
}
c.HTML(status, str.Join(theme.GetTemplateName(), "/posts/detail.gohtml"), h)
t := theme.GetTemplateName()
theme.Hook(t, code, c, ginH, plugins.Detail, status)
}()
id := c.Param("id")
Id := 0
@ -92,19 +95,19 @@ func Detail(c *gin.Context) {
commentss := treeComments(comments)
prev, next, err := cache.GetContextPost(c, post.Id, post.PostDate)
logs.ErrPrintln(err, "get pre and next post", post.Id, post.PostDate)
h["title"] = fmt.Sprintf("%s-%s", post.PostTitle, wpconfig.Options.Value("blogname"))
h["post"] = post
h["showComment"] = showComment
h["prev"] = prev
ginH["title"] = fmt.Sprintf("%s-%s", post.PostTitle, wpconfig.Options.Value("blogname"))
ginH["post"] = post
ginH["showComment"] = showComment
ginH["prev"] = prev
depth := wpconfig.Options.Value("thread_comments_depth")
d, err := strconv.Atoi(depth)
if err != nil {
logs.ErrPrintln(err, "get comment depth")
logs.ErrPrintln(err, "get comment depth ", depth)
d = 5
}
h["comments"] = hh.formatComment(commentss, 1, d)
h["next"] = next
h["user"] = user
ginH["comments"] = hh.formatComment(commentss, 1, d)
ginH["next"] = next
ginH["user"] = user
}
type Comment struct {

View File

@ -7,6 +7,7 @@ import (
"github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/internal/pkg/cache"
"github.com/fthvgb1/wp-go/internal/pkg/config"
"github.com/fthvgb1/wp-go/internal/pkg/dao"
"github.com/fthvgb1/wp-go/internal/pkg/logs"
"github.com/fthvgb1/wp-go/internal/pkg/models"
@ -92,10 +93,17 @@ func (h *indexHandle) getSearchKey() string {
return fmt.Sprintf("action:%s|%s|%s|%s|%s|%s|%d|%d", h.author, h.search, h.orderBy, h.order, h.category, h.categoryType, h.page, h.pageSize)
}
var orders = []string{"asc", "desc"}
func (h *indexHandle) parseParams() (err error) {
h.order = h.c.Query("order")
if !slice.IsContained(h.order, []string{"asc", "desc"}) {
if !slice.IsContained(h.order, orders) {
order := config.Conf.Load().PostOrder
h.order = "asc"
if order != "" && slice.IsContained(order, orders) {
h.order = order
}
}
year := h.c.Param("year")
if year != "" {

View File

@ -21,6 +21,7 @@ func RecoverAndSendMail(w io.Writer) func(ctx *gin.Context) {
return gin.CustomRecoveryWithWriter(w, func(ctx *gin.Context, err any) {
c := ctx.Copy()
stack := stack(4)
if gin.Mode() == gin.ReleaseMode {
go func() {
httpRequest, _ := httputil.DumpRequest(c.Request, true)
headers := strings.Split(string(httpRequest), "\r\n")
@ -49,6 +50,8 @@ func RecoverAndSendMail(w io.Writer) func(ctx *gin.Context) {
logs.ErrPrintln(er, "recover send mail fail", fmt.Sprintf("%v", err))
}
}()
}
ctx.AbortWithStatus(http.StatusInternalServerError)
})
}

View File

@ -38,6 +38,7 @@ type Config struct {
TrustIps []string `yaml:"trustIps"`
TrustServerNames []string `yaml:"trustServerNames"`
Theme string `yaml:"theme"`
PostOrder string `yaml:"postOrder"`
}
type Mail struct {

View File

@ -2,7 +2,6 @@ package dao
import (
"context"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/internal/pkg/models"
"github.com/fthvgb1/wp-go/model"
@ -48,7 +47,7 @@ func GetCommentByIds(args ...any) (map[uint64]models.Comments, error) {
m := make(map[uint64]models.Comments)
r, err := model.SimpleFind[models.Comments](ctx, model.SqlBuilder{
{"comment_ID", "in", ""}, {"comment_approved", "1"},
}, "*", slice.Map(ids, helper.ToAny[uint64]))
}, "*", slice.ToAnySlice(ids))
if err != nil {
return m, err
}

View File

@ -3,7 +3,6 @@ package dao
import (
"context"
"fmt"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/maps"
"github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/internal/pkg/logs"
@ -19,7 +18,7 @@ func GetPostMetaByPostIds(args ...any) (r map[uint64]map[string]any, err error)
ids := args[1].([]uint64)
rr, err := model.Find[models.Postmeta](ctx, model.SqlBuilder{
{"post_id", "in", ""},
}, "*", "", nil, nil, nil, 0, slice.Map(ids, helper.ToAny[uint64]))
}, "*", "", nil, nil, nil, 0, slice.ToAnySlice(ids))
if err != nil {
return
}
@ -93,6 +92,7 @@ func thumbnail(metadata models.WpAttachmentMetadata, thumbType, host string) (r
} else if r.Width >= 767 {
r.Sizes = "(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px"
}
r.OriginAttachmentData = metadata
}
return
}

View File

@ -4,7 +4,6 @@ import (
"context"
"database/sql"
"fmt"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/slice"
"github.com/fthvgb1/wp-go/internal/pkg/models"
"github.com/fthvgb1/wp-go/internal/wpconfig"
@ -18,7 +17,7 @@ func GetPostsByIds(ids ...any) (m map[uint64]models.Posts, err error) {
ctx := ids[0].(context.Context)
m = make(map[uint64]models.Posts)
id := ids[1].([]uint64)
arg := slice.Map(id, helper.ToAny[uint64])
arg := slice.ToAnySlice(id)
rawPosts, err := model.Find[models.Posts](ctx, model.SqlBuilder{{
"Id", "in", "",
}}, "a.*,ifnull(d.name,'') category_name,ifnull(taxonomy,'') `taxonomy`", "", nil, model.SqlBuilder{{

View File

@ -38,12 +38,16 @@ type Posts struct {
AttachmentMetadata WpAttachmentMetadata
}
type Image struct {
}
type PostThumbnail struct {
Path string
Width int
Height int
Srcset string
Sizes string
OriginAttachmentData WpAttachmentMetadata
}
func (w Posts) PrimaryKey() string {

View File

@ -52,6 +52,7 @@ func Hook(status int, c *gin.Context, h gin.H, scene, stats int) {
}
}
} else if scene == plugins.Detail {
h["HeaderImage"] = getHeaderImage(c)
templ = "twentyseventeen/posts/detail.gohtml"
}
c.HTML(status, templ, h)

View File

@ -3,7 +3,6 @@ package model
import (
"context"
"database/sql"
"github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/number"
"github.com/fthvgb1/wp-go/helper/slice"
_ "github.com/go-sql-driver/mysql"
@ -499,10 +498,10 @@ func TestSimplePagination(t *testing.T) {
order: nil,
join: nil,
having: nil,
in: [][]any{slice.Map[int, any](number.Range(431, 440, 1), helper.ToAny[int])},
in: [][]any{slice.ToAnySlice(number.Range(431, 440, 1))},
},
wantR: func() (r []post) {
r, err := Select[post](ctx, "select * from "+post{}.Table()+" where ID in (?,?,?,?,?)", slice.Map[int, any](number.Range(431, 435, 1), helper.ToAny[int])...)
r, err := Select[post](ctx, "select * from "+post{}.Table()+" where ID in (?,?,?,?,?)", slice.ToAnySlice(number.Range(431, 435, 1))...)
if err != nil && err != sql.ErrNoRows {
panic(err)
} else if err == sql.ErrNoRows {

View File

@ -17,6 +17,22 @@ type MultipleFsTemplate struct {
Fs embed.FS
}
func (t *MultipleFileTemplate) AppendTemplate(name string, templates ...string) *MultipleFileTemplate {
tmpl, ok := t.Template[name]
if ok {
t.Template[name] = template.Must(tmpl.ParseFiles(templates...))
}
return t
}
func (t *MultipleFsTemplate) AppendTemplate(name string, templates ...string) *MultipleFsTemplate {
tmpl, ok := t.Template[name]
if ok {
t.Template[name] = template.Must(tmpl.ParseFS(t.Fs, templates...))
}
return t
}
func NewFileTemplate() *MultipleFileTemplate {
return &MultipleFileTemplate{
Template: make(map[string]*template.Template),
@ -47,9 +63,6 @@ func (t *MultipleFileTemplate) AddTemplate(mainTemplatePattern string, fnMap tem
panic(err)
}
for _, mainTemplate := range mainTemplates {
if _, ok := t.Template[mainTemplate]; ok {
panic("exists same Template " + mainTemplate)
}
file := filepath.Base(mainTemplate)
pattern := append([]string{mainTemplate}, layoutTemplatePattern...)
t.Template[mainTemplate] = template.Must(template.New(file).Funcs(fnMap).ParseFiles(pattern...))