|
- package slideimage
-
- import (
- "bytes"
- "image"
- "image/png"
- "math"
- "math/rand"
- "path"
- "strconv"
- "strings"
- "time"
-
- "code.gitea.io/gitea/modules/labelmsg"
- "github.com/gomodule/redigo/redis"
-
- "code.gitea.io/gitea/modules/public"
-
- "code.gitea.io/gitea/modules/log"
-
- "code.gitea.io/gitea/modules/setting"
-
- "code.gitea.io/gitea/modules/redis/redis_client"
-
- "gitea.com/macaron/macaron"
- "github.com/disintegration/imaging"
- "github.com/google/uuid"
- )
-
- type SlideImage struct {
- SubURL string
- URLPrefix string
- SampleImages int
- StdWidth int
- StdHeight int
- MaskSize int
- ImageY int
- ImageXStart int
- MinImageX int
- Expiration int
- Tolerance int
- CachePrefix string
- CacheManualPrefix string
- }
-
- type Options struct {
- // Suburl path. Default is empty.
- SubURL string
- // URL prefix of getting captcha pictures. Default is "/slideimage/".
- URLPrefix string
- //Default is 4
- SampleImages int
- //Image width default 391
- StdWidth int
- //Image Height default 196
- StdHeight int
- // default 51
- MaskSize int
- // default 125
- ImageY int
- //default 0
- ImageXStart int
- //容忍的误差 default 2px
- Tolerance int
- // default 150
- MinImageX int
- // default 600 seconds
- Expiration int
- //default slide:
- CachePrefix string
- //default mslide: 验证通过,在缓存中记录已进行过人工操作,然后在发送验证码之前再进行校验是否进行了人工操作。
- CacheManualPrefix string
- }
-
- func NewSlideImage(opt Options) *SlideImage {
- return &SlideImage{
- SubURL: opt.SubURL,
- URLPrefix: opt.URLPrefix,
- SampleImages: opt.SampleImages,
- StdWidth: opt.StdWidth,
- StdHeight: opt.StdHeight,
- MaskSize: opt.MaskSize,
- ImageY: opt.ImageY,
- ImageXStart: opt.ImageXStart,
- Tolerance: opt.Tolerance,
- MinImageX: opt.MinImageX,
- Expiration: opt.Expiration,
- CachePrefix: opt.CachePrefix,
- }
- }
-
- func prepareOptions(options []Options) Options {
- var opt Options
- if len(options) > 0 {
- opt = options[0]
- }
-
- opt.SubURL = strings.TrimSuffix(opt.SubURL, "/")
-
- // Defaults.
- if len(opt.URLPrefix) == 0 {
- opt.URLPrefix = "/slideimage/"
- } else if opt.URLPrefix[len(opt.URLPrefix)-1] != '/' {
- opt.URLPrefix += "/"
- }
- if opt.SampleImages == 0 {
- opt.SampleImages = 4
- }
- if opt.StdWidth == 0 {
- opt.StdWidth = 391
- }
- if opt.StdHeight == 0 {
- opt.StdHeight = 196
- }
- if opt.MaskSize == 0 {
- opt.MaskSize = 51
- }
- if opt.ImageY == 0 {
- opt.ImageY = 75
- }
- if opt.ImageXStart == 0 {
- opt.ImageXStart = 2
- }
-
- if opt.Tolerance == 0 {
- opt.Tolerance = 2
- }
-
- if opt.MinImageX == 0 {
- opt.MinImageX = 150
- }
- if opt.Expiration == 0 {
- opt.Expiration = 600
- }
-
- if len(opt.CachePrefix) == 0 {
- opt.CachePrefix = "slide:"
- }
- if len(opt.CacheManualPrefix) == 0 {
- opt.CacheManualPrefix = "mslide:"
- }
-
- return opt
- }
-
- func (s *SlideImage) key(id string) string {
- return s.CachePrefix + id
- }
- func (s *SlideImage) mkey(id string) string {
- return s.CacheManualPrefix + id
- }
-
- func (s *SlideImage) VerifyManual(id string) (bool, error) {
- redisConn := labelmsg.Get()
- defer redisConn.Close()
- return redis_client.EXISTS(redisConn, s.mkey(id))
- }
-
- func (s *SlideImage) Verify(id string, x int) bool {
- redisConn := labelmsg.Get()
- defer redisConn.Close()
-
- v, err := redis_client.GET(redisConn, s.key(id))
- v1, err := redis.String(v, err)
- if err != nil {
- log.Warn("redis err", err)
- return false
- }
- if v1 == "" {
- return false
- }
- values := strings.Split(v1, "-")
- imageRandX, _ := strconv.Atoi(values[1])
- if int(math.Abs(float64(imageRandX-x))) <= s.Tolerance {
- redis_client.SETNX(redisConn, s.mkey(id), "1", s.Expiration)
- return true
- }
- return false
-
- }
-
- func (s *SlideImage) CreateCode() (string, int, int) {
- nums := rand.Intn(s.SampleImages)
- imageId := uuid.New().String()
-
- //获取随机x坐标
- imageRandX := rand.Intn(s.StdWidth - s.MaskSize - 3)
- if imageRandX < s.MinImageX {
- imageRandX += s.MinImageX
- }
- redis_client.Setex(s.key(imageId), strconv.Itoa(nums)+"-"+strconv.Itoa(imageRandX), time.Second*time.Duration(s.Expiration))
- return imageId, nums, imageRandX
- }
-
- func SlideImager(options ...Options) macaron.Handler {
- return func(ctx *macaron.Context) {
- slideImage := NewSlideImage(prepareOptions(options))
-
- if strings.HasPrefix(ctx.Req.URL.Path, slideImage.URLPrefix) {
- id := path.Base(ctx.Req.URL.Path)
- if i := strings.Index(id, "."); i > -1 {
- id = id[:i]
- }
- isScreenshot := strings.HasSuffix(id, "screenshot")
- if isScreenshot {
- id = strings.TrimSuffix(id, "screenshot")
- }
-
- key := slideImage.key(id)
- v, err := redis_client.Get(key)
-
- if err != nil || v == "" {
- ctx.Status(404)
- ctx.Write([]byte("not found"))
- //png.Encode(ctx.Resp, *setting.SlideImagesBg[0])
- return
- }
-
- values := strings.Split(v, "-")
-
- imageIndex, _ := strconv.Atoi(values[0])
- imageRandX, _ := strconv.Atoi(values[1])
- imageBg := setting.SlideImagesBg[imageIndex]
-
- maxPotion := image.Point{
- X: imageRandX + slideImage.MaskSize,
- Y: slideImage.ImageY + slideImage.MaskSize,
- }
- minPotion := image.Point{
- X: imageRandX,
- Y: slideImage.ImageY,
- }
- subimg := image.Rectangle{
- Max: maxPotion,
- Min: minPotion,
- }
-
- if isScreenshot {
- data := imaging.Crop(*imageBg, subimg)
- png.Encode(ctx.Resp, data)
-
- } else {
- data := imaging.Overlay(*imageBg, *setting.SlideMaskImage, minPotion, 1.0)
- png.Encode(ctx.Resp, data)
- }
- ctx.Status(200)
- return
-
- }
- ctx.Data["SlideImageInfo"] = slideImage
- ctx.Data["EnablePhone"] = setting.PhoneService.Enabled
- ctx.Map(slideImage)
-
- }
- }
-
- func InitSlideImage() {
- if setting.PhoneService.Enabled {
-
- filenames, err := public.Dir(path.Join("img", "slide", "bg"))
- if err != nil {
- panic("Slide Image Service init failed")
- }
- maskFileName, err := public.Dir(path.Join("img", "slide", "mask"))
- if err != nil {
- panic("Slide Image Service init failed")
- }
-
- for _, filename := range filenames {
- if strings.HasSuffix(filename, ".png") {
- content, err := public.Asset(path.Join("img", "slide", "bg", filename))
-
- if err != nil {
- log.Warn("can not open "+filename, err)
- continue
- }
-
- m, err := png.Decode(bytes.NewReader(content))
-
- if err != nil {
- log.Warn("can not decode "+filename, err)
- continue
- }
- setting.SlideImagesBg = append(setting.SlideImagesBg, &m)
- }
-
- }
- setting.SlideImagesCount = len(setting.SlideImagesBg)
- if setting.SlideImagesCount == 0 {
- panic("Slide Image Service init failed")
- }
-
- maskContent, err := public.Asset(path.Join("img", "slide", "mask", maskFileName[0]))
-
- if err != nil {
- panic("Slide Image Service init failed")
- }
-
- MaskImage, err := png.Decode(bytes.NewReader(maskContent))
- if err != nil {
- panic("Slide Image Service init failed")
- }
- setting.SlideMaskImage = &MaskImage
-
- log.Info("Slide Image Service Enabled")
-
- }
- }
|