3 커밋 bb45d4c82c ... 77cc8311a8

작성자 SHA1 메시지 날짜
  hakjinlee 77cc8311a8 solution-type-get, product-page-get 구현 및 테마 추가 1 년 전
  hakjinlee 96aca74838 Merge branch 'master' of http://git.daboryhost.com:10880/dabory/kkscrap-go 1 년 전
  hakjinlee 4741e96d5c .. 1 년 전

+ 9 - 1
.idea/erp-kkscrap.iml

@@ -1,6 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <module type="WEB_MODULE" version="4">
-  <component name="Go" enabled="true" />
+  <component name="Go" enabled="true">
+    <buildTags>
+      <option name="customFlags">
+        <array>
+          <option value="dev" />
+        </array>
+      </option>
+    </buildTags>
+  </component>
   <component name="NewModuleRootManager">
     <content url="file://$MODULE_DIR$" />
     <orderEntry type="inheritedJdk" />

+ 1 - 4
controllers/scraper/cafe24/parser.go

@@ -9,10 +9,7 @@ import (
 	"github.com/PuerkitoBio/goquery"
 )
 
-func Parse(uri string, item *model.ItemInfo) {
-	body, err := util.Get(uri)
-	util.CheckError(err)
-
+func Parse(body string, item *model.ItemInfo) {
 	item.Language = common.GetLanguage(body)
 
 	doc, err := goquery.NewDocumentFromReader(strings.NewReader(body))

+ 1 - 4
controllers/scraper/godo/parser.go

@@ -11,10 +11,7 @@ import (
 	"github.com/PuerkitoBio/goquery"
 )
 
-func Parse(uri string, item *model.ItemInfo) {
-	body, err := util.Get(uri)
-	util.CheckError(err)
-
+func Parse(body string, item *model.ItemInfo) {
 	item.Language = getLanguage(body)
 
 	doc, err := goquery.NewDocumentFromReader(strings.NewReader(body))

+ 1 - 4
controllers/scraper/magento/parser.go

@@ -10,10 +10,7 @@ import (
 	"github.com/PuerkitoBio/goquery"
 )
 
-func Parse(uri string, item *model.ItemInfo) {
-	body, err := util.Get(uri)
-	util.CheckError(err)
-
+func Parse(body string, item *model.ItemInfo) {
 	item.Language = common.GetLanguage(body)
 
 	doc, err := goquery.NewDocumentFromReader(strings.NewReader(body))

+ 1 - 4
controllers/scraper/shopify/parser.go

@@ -10,10 +10,7 @@ import (
 	"github.com/PuerkitoBio/goquery"
 )
 
-func Parse(uri string, item *model.ItemInfo) {
-	body, err := util.Get(uri)
-	util.CheckError(err)
-
+func Parse(body string, item *model.ItemInfo) {
 	item.Language = common.GetLanguage(body)
 
 	doc, err := goquery.NewDocumentFromReader(strings.NewReader(body))

+ 99 - 39
controllers/scraper/tct-main.go

@@ -7,18 +7,16 @@ import (
 	"kkscrap-go/controllers/scraper/godo"
 	"kkscrap-go/controllers/scraper/magento"
 	"kkscrap-go/controllers/scraper/shopify"
-	"kkscrap-go/locals"
-
+	"kkscrap-go/controllers/scraper/util"
 	"kkscrap-go/controllers/scraper/wordpress"
 	"kkscrap-go/controllers/scraper/young"
+	"kkscrap-go/locals"
 	"kkscrap-go/model"
 	"net/http"
 	"net/url"
 	"regexp"
 	"strings"
 
-	util "kkscrap-go/controllers/scraper/util"
-
 	// "golang.org/x/crypto/bcrypt"
 
 	"github.com/labstack/echo"
@@ -37,18 +35,23 @@ func SolutionTypeGet(c echo.Context) error {
 		ThemeType    string
 	}{}
 
+	body, err := util.Get(v.Url)
+	if err != nil {
+		return err
+	}
+
 	fmt.Println(v.Url)
-	// (1) function 공유될 수 있도록 해서 작업요
-	// v.SolutionType, v.ThemeType = solType(&kkk)
-	retv.SolutionType = "Wordpress"
-	retv.ThemeType = "Avada"
-	// ret, _ := json.MarshalIndent(itemInfo, "", "\t")
-	ret, _ := json.Marshal(retv)
-	return c.JSONBlob(http.StatusOK, ret)
+	st, theme, err := getSolutionType(body)
+	if err != nil {
+		return c.JSONBlob(http.StatusOK, []byte(err.Error()))
+	}
+	retv.SolutionType = string(st)
+	retv.ThemeType = theme
+	return c.JSON(http.StatusOK, retv)
 }
 
 type ProductPageGetReq struct {
-	SolutionType string
+	SolutionType model.SolutionType
 	ThemeType    string
 	Products     []ProductUri
 }
@@ -65,22 +68,28 @@ func ProductPageGet(c echo.Context) error {
 	v := c.Get("receiver").(ProductPageGetReq)
 
 	var vRet locals.ProductPage // Row(개별레코드)->Page(Row의 집합)->Book(Page의 집합)의 개념
-	for _, row := range v.Products {
+	for _, p := range v.Products {
 
-		// (1)Url 의 HTML를 2번 가져오는데 아래와 같이 1번만 가져와서 처리할 수 있도록 수정요.
-		// kkk := htmlGet(row.Uri)
+		body, err := util.Get(p.Uri)
+		if err != nil {
+			return err
+		}
 
 		// 전체 웹사이트가 아니라 개별 상품페이지(1개페이지)의 경우 SolutionType 없이 request됨
 		if v.SolutionType == "" {
-			// v.SolutionType, v.ThemeType = solType(&kkk)
+			var err error
+			v.SolutionType, v.ThemeType, err = getSolutionType(body)
+			if err != nil {
+				return c.String(http.StatusBadRequest, err.Error())
+			}
 		}
 
-		prodInfo, err := parse(row.Uri)
+		prodInfo, err := parse(p.Uri, body)
 		if err != nil {
 			prodInfo.ItemNick = "Parsing Failed"
 		}
 		// 개별 prodInfo가 계속 추가될 수 있도록 코드를 변경요.
-		// vRet.ProductPage = append(vRet.ProductPage, *prodInfo)
+		vRet.ProductPage = append(vRet.ProductPage, toProductPage(prodInfo))
 	}
 
 	// ret, _ := json.MarshalIndent(itemInfo, "", "\t")
@@ -88,6 +97,51 @@ func ProductPageGet(c echo.Context) error {
 	return c.JSONBlob(http.StatusOK, ret)
 }
 
+func toProductPage(info model.ItemInfo) locals.Product {
+	ret := locals.Product{
+		SolutionName:  locals.SolutionType(info.SolutionName),
+		Version:       info.Version,
+		Emails:        info.Emails,
+		DomainName:    info.DomainName,
+		DomainURI:     info.DomainURI,
+		ItemName:      info.ItemName,
+		ItemNick:      info.ItemNick,
+		ModelName:     info.ModelName,
+		ModelNo:       info.ModelNo,
+		BrandName:     info.BrandName,
+		Sku:           info.Sku,
+		ItemCategory:  info.ItemCategory,
+		Manufacturer:  info.Manufacturer,
+		Origin:        info.Origin,
+		Language:      info.Language,
+		Currency:      info.Currency,
+		SalesPrice:    info.SalesPrice,
+		DeliveryPrice: info.DeliveryPrice,
+		MinimumQty:    info.MinimumQty,
+		UserCredit:    info.UserCredit,
+		Options:       nil,
+		Images:        info.Images,
+		ShortDesc:     info.ShortDesc,
+		OriginDesc:    info.OriginDesc,
+		TextDesc:      info.TextDesc,
+	}
+
+	for i, v := range info.Options {
+		ret.Options = append(ret.Options, locals.Option{
+			Name: v.Name,
+		})
+		ret.Options[i].Choices = make([]locals.Choice, 0)
+		for _, choice := range v.Choices {
+			ret.Options[i].Choices = append(ret.Options[i].Choices, locals.Choice{
+				Name:  choice.Name,
+				Price: choice.Price,
+			})
+		}
+	}
+
+	return ret
+}
+
 type ItemUrlScrapReq struct {
 	ItemUrl string
 }
@@ -96,11 +150,12 @@ func ItemUrlScrap(c echo.Context) error {
 
 	v := c.Get("receiver").(ItemUrlScrapReq)
 
-	// retv := &struct {
-	// 	model.ItemInfo
-	// }{}
+	body, err := util.Get(v.ItemUrl)
+	if err != nil {
+		return err
+	}
 
-	itemInfo, err := parse(v.ItemUrl)
+	itemInfo, err := parse(v.ItemUrl, body)
 	if err != nil {
 		return c.String(604, "ertvwerawqfd-ItemUrl Parse failed: "+err.Error())
 	}
@@ -125,16 +180,16 @@ func getTitle(body string) string {
 	return ""
 }
 
-func parse(uri string) (ret model.ItemInfo, err error) {
-	t, err := getSolutionType(uri)
+func parse(uri, body string) (ret model.ItemInfo, err error) {
+	t, theme, err := getSolutionType(body)
 	if err != nil {
 		return
 	}
-	ret = parseSolution(t, uri)
+	ret = parseSolution(t, theme, uri, body)
 	return
 }
 
-func parseSolution(t model.SolutionType, uri string) (ret model.ItemInfo) {
+func parseSolution(t model.SolutionType, theme, uri, body string) (ret model.ItemInfo) {
 	ret.SolutionName = t
 	u, err := url.Parse(uri)
 	if err != nil {
@@ -144,29 +199,24 @@ func parseSolution(t model.SolutionType, uri string) (ret model.ItemInfo) {
 	ret.DomainURI = uri
 
 	if t == model.SolutionTypeWooCommerce {
-		wordpress.Parse(uri, &ret)
+		wordpress.Parse(body, &ret)
 	} else if t == model.SolutionTypeShopify {
-		shopify.Parse(uri, &ret)
+		shopify.Parse(body, &ret)
 	} else if t == model.SolutionTypeMagento {
-		magento.Parse(uri, &ret)
+		magento.Parse(body, &ret)
 	} else if t == model.SolutionTypeCafe24 {
-		cafe24.Parse(uri, &ret)
+		cafe24.Parse(body, &ret)
 	} else if t == model.SolutionTypeGodo {
-		godo.Parse(uri, &ret)
+		godo.Parse(body, &ret)
 	} else if t == model.SolutionTypeYoung {
-		young.Parse(uri, &ret)
+		young.Parse(body, &ret)
 	} else if t == model.SolutionTypeOthers {
-		magento.Parse(uri, &ret)
+		magento.Parse(body, &ret)
 	}
 	return
 }
 
-func getSolutionType(url string) (t model.SolutionType, reterr error) {
-	body, err := util.Get(url)
-	if err != nil {
-		reterr = err
-		return
-	}
+func getSolutionType(body string) (t model.SolutionType, theme string, reterr error) {
 	if strings.Contains(body, "window.CAFE24") {
 		t = model.SolutionTypeCafe24
 	} else if strings.Contains(body, "woocommerce-page") {
@@ -184,6 +234,16 @@ func getSolutionType(url string) (t model.SolutionType, reterr error) {
 		//reterr = errors.New("no found solution type")
 	}
 
+	theme = ""
+	re := regexp.MustCompile(`wp-content\/themes\/(.+?)\/`)
+	result := re.FindStringSubmatch(body)
+
+	if len(result) > 1 {
+		theme = result[1]
+	} else {
+		theme = "generic"
+	}
+
 	//ioutil.WriteFile(string(t) + ".html", []byte(body), 644)
 	return
 }

+ 46 - 0
controllers/scraper/tct_test.go

@@ -0,0 +1,46 @@
+package controllers_scraper
+
+import (
+	"kkscrap-go/model"
+	"testing"
+)
+
+func TestToProduct(t *testing.T) {
+	ori := model.ItemInfo{
+		SolutionName:  "hello",
+		Version:       "",
+		Emails:        nil,
+		DomainName:    "",
+		DomainURI:     "",
+		ItemName:      "",
+		ItemNick:      "",
+		ModelName:     "",
+		ModelNo:       "",
+		BrandName:     "",
+		Sku:           "",
+		ItemCategory:  "",
+		Manufacturer:  "",
+		Origin:        "",
+		Language:      "",
+		Currency:      "",
+		SalesPrice:    0,
+		DeliveryPrice: 0,
+		MinimumQty:    0,
+		UserCredit:    0,
+		Options:       nil,
+		Images:        nil,
+		ShortDesc:     "",
+		OriginDesc:    "",
+		TextDesc:      "1234",
+	}
+
+	pro := toProductPage(ori)
+
+	if pro.TextDesc != "1234" {
+		t.Error("TextDesc is not equal")
+	}
+
+	if pro.SolutionName != "hello" {
+		t.Error("SolutionType is not equal")
+	}
+}

+ 20 - 3
controllers/scraper/util/util.go

@@ -21,12 +21,23 @@ func Get(url string) (body string, err error) {
 		return
 	}
 
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return "", err
+	}
+	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36")
+
 	fmt.Println("url:", url)
-	resp, err := http.Get(url)
-	if err != nil || resp.StatusCode != 200 {
-		err = errors.New("this site is not available\n" + url)
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		msg := "this site is not available\n" + url
+		if err != nil {
+			msg += "\n" + err.Error()
+		}
+		err = errors.New(msg)
 		return
 	}
+
 	var data []byte
 	defer resp.Body.Close()
 	data, err = ioutil.ReadAll(resp.Body)
@@ -34,6 +45,12 @@ func Get(url string) (body string, err error) {
 		return
 	}
 	body = string(data)
+
+	if resp.StatusCode != 200 {
+		msg := "this site is not available\n" + url + "\n" + body
+		err = errors.New(msg)
+		return
+	}
 	return
 }
 

+ 8 - 6
controllers/scraper/wordpress/parser.go

@@ -11,19 +11,21 @@ import (
 	"strings"
 )
 
-func Parse(uri string, item *model.ItemInfo) {
-	body, err := util.Get(uri)
-	util.CheckError(err)
+func Parse(body string, item *model.ItemInfo) {
 	p := getProduct(body)
-	item.Images = append(item.Images, p.Image)
+	if p.Image != "" {
+		item.Images = append(item.Images, p.Image)
+	}
 	item.Images = append(item.Images, getImages(body)...)
-	item.SalesPrice = util.GetFloat32(p.Offers[0].Price)
+	if p.Offers != nil && len(p.Offers) > 0 {
+		item.SalesPrice = util.GetFloat32(p.Offers[0].Price)
+		item.Currency = p.Offers[0].Pricecurrency
+	}
 	item.Sku = fmt.Sprintf("%v", p.Sku)
 	item.ShortDesc = p.Description
 	item.OriginDesc = getProductDescription(body)
 	item.TextDesc = getTextDesc(body)
 	item.ItemName = p.Name
-	item.Currency = p.Offers[0].Pricecurrency
 	item.Language = common.GetLanguage(body)
 	item.Emails = common.GetEmails(body)
 	item.Options = append(item.Options, getOptions(body))

+ 1 - 4
controllers/scraper/young/parser.go

@@ -11,10 +11,7 @@ import (
 	"github.com/PuerkitoBio/goquery"
 )
 
-func Parse(uri string, item *model.ItemInfo) {
-	body, err := util.Get(uri)
-	util.CheckError(err)
-
+func Parse(body string, item *model.ItemInfo) {
 	item.Language = common.GetLanguage(body)
 
 	doc, err := goquery.NewDocumentFromReader(strings.NewReader(body))

+ 491 - 0
locals/common-func-dev.go

@@ -0,0 +1,491 @@
+package locals
+
+import (
+	"bytes"
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/dabory/abango-rest"
+	e "github.com/dabory/abango-rest/etc"
+	"github.com/go-xorm/xorm"
+	"github.com/microcosm-cc/bluemonday"
+)
+
+const (
+	// Tpf string = "dbr_" // TablePrefix
+	//QueryComment
+	QcWhere        string = "-- @where"
+	QcSubWhere     string = "-- @subwhere"
+	QcHaving       string = "-- @having"
+	QcOrder        string = "-- @order"
+	QcLimitOffset  string = "-- @limitoffset"
+	QcExtract      string = "-- @extract:"
+	QcClosed       string = "-- @closed:"
+	QcDelivery     string = "-- @delivery:"
+	QcBetweenDates string = "-- @between_dates"
+	QcEnd          string = "--" //QueryComment
+	//QueryKeyword
+	QkWhere string = "\nwhere true "
+	// QkWhere  string = "\nwhere 1 "
+	QkHaving string = "\nhaving true "
+	QkOrder  string = "\norder by "
+	QkLimit  string = "\nlimit  "
+	QkOffset string = " offset "
+
+	QkTmpOrder string = " order by is_sum desc, t_id asc "
+)
+
+type AppApi struct {
+	ApiUrl    string
+	GateToken string
+}
+
+// 0:Sso, 1:Dbu  매우 중요하다.
+var gAppApis [2]AppApi
+
+var (
+	SQL_DEBUG    bool
+	NORMAL_DEBUG bool
+)
+
+var BlockNoCnt int
+
+// type GateTokenGetReq struct {
+// 	ClientId     string
+// 	BeforeBase64 string
+// 	AppBase64    string
+// }
+
+func (y *SyncController) Init() error {
+
+	vmc := &struct {
+		Driver   string
+		Host     string
+		Port     int
+		Username string
+		Database string
+		Password string
+	}{}
+
+	connStr := vmc.Username + ":" + vmc.Password + "@tcp(" + vmc.Host + ":" + e.NumToStr(vmc.Port) + ")/" + vmc.Database
+
+	fmt.Println("connStr:", connStr)
+	y.Scb.ConnString = connStr
+
+	var err error
+	if y.Db, err = xorm.NewEngine(abango.XConfig["DbType"], connStr); err != nil {
+		return e.ErrLog(e.FuncRun("309upajs3w: DBEngine Open Error ", e.CurrFuncName()), err)
+	}
+
+	var connHint string
+	strArr := strings.Split(connStr, "@tcp")
+	if len(strArr) == 2 {
+		connHint = strArr[1]
+	} else {
+		return e.ErrLog(e.FuncRun("309upajs3w: connString format mismatch: "+strArr[1], e.CurrFuncName()), err)
+	}
+
+	y.Db.ShowSQL(false)
+	y.Db.SetMaxOpenConns(100)
+	y.Db.SetMaxIdleConns(20)
+	y.Db.SetConnMaxLifetime(60 * time.Second)
+	if _, err := y.Db.IsTableExist("aaa"); err == nil {
+		e.OkLog("SyncDB connection in " + connHint)
+		return nil
+	} else {
+		return e.ErrLog(e.FuncRun("93haoy93d: SyncDB connection Fail in "+connHint+": ", e.CurrFuncName()), err)
+	}
+
+}
+
+type (
+	MemoryMap map[string]interface{}
+
+	MapStore struct {
+		store MemoryMap
+	}
+)
+
+func (c *MapStore) Get(key string) interface{} {
+	return c.store[key]
+}
+
+func (c *MapStore) Set(key string, val interface{}) {
+	if c.store == nil {
+		c.store = make(MemoryMap)
+	}
+	c.store[key] = val
+}
+
+func DbrPasswd(password string, salt string) string {
+	salt16 := DbrSaltBase(salt, 16)
+	var passwordBytes = []byte(password)
+	var sha256Hasher = sha256.New()
+
+	passwordBytes = append(passwordBytes, salt16...)
+	sha256Hasher.Write(passwordBytes)
+
+	var hashedPasswordBytes = sha256Hasher.Sum(nil)
+	var base64EncodedPasswordHash = base64.URLEncoding.EncodeToString(hashedPasswordBytes)
+
+	return base64EncodedPasswordHash
+}
+
+func DbrHashedIndex(target string) string {
+	//!!중요: salt는 16char에서만 작동된다. hash 값은 44 char나오지만 32char로 잘라서 쓴다.
+	fmt.Println("hash_full_length:", DbrPasswd(target, "$$hashed_index$$"))
+	return DbrPasswd(target, "$$hashed_index$$")[0:32]
+}
+
+func DbrCompare(hashedPassword, currPassword string, salt string) bool {
+	// fmt.Println("salt:", salt)
+	// fmt.Println("currPassword:", currPassword)
+	var currPasswordHash = DbrPasswd(currPassword, salt)
+	// fmt.Println("currPasswordHash:", currPasswordHash)
+	// fmt.Println("hashedPassword:", hashedPassword)
+	return hashedPassword == currPasswordHash
+}
+
+func DbrSaltBase(salt string, saltSize int) []byte { //어떤 사이즈라도 16byte의 Base64로 변경
+	tmp := []byte(salt)
+	salt64 := base64.StdEncoding.EncodeToString(tmp)
+	return []byte(salt64[4 : saltSize+4])
+}
+
+func HasPickActPage(uri string, table string) bool {
+	if table == "member" {
+		if uri == "/"+table+"-pick" || uri == "/"+table+"-act" || uri == "/"+table+"-page" || uri == "/"+table+"-secured-pick" || uri == "/"+table+"-secured-page" || uri == "/"+table+"-secured-act" {
+			return true
+		} else {
+			return false
+		}
+	} else {
+		if uri == "/"+table+"-pick" || uri == "/"+table+"-act" || uri == "/"+table+"-page" {
+			return true
+		} else {
+			return false
+		}
+	}
+}
+
+// func ByteIndex(ba *[]byte, bt byte, opt int) int {
+// 	if opt == 0 { //normal
+// 		for i := 0; i < len(*ba); i++ {
+// 			if (*ba)[i] == bt {
+// 				return i
+// 			}
+// 		}
+// 	} else if opt == 1 { //rerverse
+// 		for i := len(*ba) - 1; i > 0; i-- {
+// 			if (*ba)[i] == bt {
+// 				return i
+// 			}
+// 		}
+// 	}
+// 	return -1
+// }
+
+func LastQry(qry xorm.Session) string {
+	ret, _ := qry.LastSQL()
+	fmt.Println("\n" + ret + "\n")
+	return ret
+}
+
+func ShowQry(qry xorm.Session, qryName string) string {
+	if SQL_DEBUG {
+		ret, _ := qry.LastSQL()
+		return e.LogStr("", "ShowQry===["+qryName+"]==="+"\n[ "+ret+" ]\n")
+	}
+	return ""
+}
+
+func ShowSql(sqlStr string, qryName string) string {
+	if SQL_DEBUG {
+		return e.LogStr("", "ShowSql===["+qryName+"]==="+"\n[ "+sqlStr+" ]\n")
+	}
+	return ""
+}
+
+// func ShowDebug(debugStr string, index string) string {
+// 	if NORMAL_DEBUG {
+// 		return e.LogStr("", "ShowDebug===["+index+"]==="+"\n[ "+debugStr+" ]\n")
+// 	}
+// 	return ""
+// }
+
+func QryDirName(qryName string) (string, string) {
+	if !strings.Contains(qryName, "::") {
+		return "queries/", qryName
+	} else {
+		q := strings.Split(qryName, "::")
+		return "queries/themes/" + q[0] + "/", q[1]
+	}
+}
+
+func StripHtml(cont string, max int) string {
+	p := bluemonday.StripTagsPolicy()
+	s := p.Sanitize(cont)
+	if len(s) > max {
+		return string([]rune(s)[:max])
+	} else {
+		return s
+	}
+}
+
+func Sanitize(cont string) string {
+	p := bluemonday.UGCPolicy()
+	return p.Sanitize(cont)
+}
+
+func AddStrIfNotExist(s *string, target string) {
+	if !strings.Contains(*s, target) {
+		*s += target
+	}
+}
+
+func HttpResponseSimplePost(method string, apiurl string, jsBytes []byte) (retbody []byte, retsta int, reterr error) {
+
+	response, err := http.Post(apiurl, "application/json", bytes.NewBuffer(jsBytes))
+	if err != nil {
+		return nil, 0, errors.New(e.FuncRunErr("65rfg0csdew", "The HTTP request failed with error "+e.CurrFuncName()+err.Error()))
+	} else {
+		retbody, err = ioutil.ReadAll(response.Body)
+		if err != nil {
+			return nil, 0, errors.New(e.FuncRunErr("kjda89382", "ReadAll error "+e.CurrFuncName()+err.Error()))
+		}
+	}
+	return retbody, response.StatusCode, nil
+}
+
+func HttpResponseWithGt(method string, apiurl string, jsBytes []byte, gateToken string) (retbody []byte, retsta int, reterr error) {
+	reader := bytes.NewBuffer(jsBytes)
+	req, err := http.NewRequest(method, apiurl, reader)
+	if err != nil {
+		return nil, 909, e.ErrLog(e.FuncRun("xcawrq3276fa-http.NewRequest "+apiurl, e.CurrFuncName()), err)
+	}
+
+	req.Header.Add("RemoteIp", "localhost")
+	req.Header.Add("Referer", "http://localhost")
+	req.Header.Add("GateToken", gateToken)
+
+	req.Body = ioutil.NopCloser(bytes.NewReader(jsBytes))
+
+	// Client객체에서 Request 실행
+	client := &http.Client{
+		Timeout: time.Second * 20, //Otherwirse, it can cause crash without this line. Must Must.
+	} // Normal is 10 but extend 20 on 1 Dec 2018
+
+	// fmt.Println(reflect.TypeOf(respo))
+	resp, err := client.Do(req)
+	if err != nil {
+		return nil, 909, e.ErrLog(e.FuncRun("wewer2354e-client.Do "+apiurl, e.CurrFuncName()), err)
+	}
+	defer resp.Body.Close()
+
+	byteRtn, _ := ioutil.ReadAll(resp.Body)
+	return byteRtn, resp.StatusCode, nil
+}
+
+func GuestGateTokenGet(appType int, pivotUrl string, ab64 string) (string, string, error) {
+
+	if gAppApis[appType].GateToken == "" {
+		req := &struct {
+			AppType   string
+			AppBase64 string
+		}{
+			AppType:   "Main",
+			AppBase64: ab64,
+		}
+
+		bodyBytes, _ := json.Marshal(req)
+		apiUrl := pivotUrl + "/gate-token-get"
+		msgBytes, staInt, err := HttpResponseSimplePost("POST", apiUrl, bodyBytes)
+		// fmt.Println("apiUrl:", apiUrl)
+		// fmt.Println("bodyBytes:", string(bodyBytes))
+
+		if err != nil {
+			return "", "", e.ErrLog(e.FuncRun("45425fd34sd-The HTTP request "+apiUrl, e.CurrFuncName()), err)
+		}
+
+		if staInt != 200 {
+			return "", "", errors.New(e.FuncRun("87ty344ra3-Request Fail "+string(msgBytes), e.CurrFuncName()))
+		}
+
+		ret := &struct {
+			ApiUrl    string
+			GateToken string
+		}{}
+
+		if err := json.Unmarshal(msgBytes, ret); err != nil {
+			return "", "", e.ErrLog(e.FuncRun("45425fd34sd-Json Format "+apiUrl, e.CurrFuncName()), err)
+		}
+		gAppApis[appType].ApiUrl = ret.ApiUrl
+		gAppApis[appType].GateToken = ret.GateToken
+
+	} else {
+		fmt.Println("GateToken already is in the ARRAY")
+	}
+
+	// fmt.Println("gSsoApiUrl:", gSsoApiUrl)
+	// fmt.Println("gSsoGateToken:", gSsoGateToken)
+
+	return gAppApis[appType].ApiUrl, gAppApis[appType].GateToken, nil
+}
+
+func GuestEncryptGet(code string) (string, string, error) {
+
+	appType := 1 //Dbupdate
+	req := &struct {
+		EncryptCode string
+	}{
+		EncryptCode: code,
+	}
+
+	// 0:Sso, 1:Dbu
+	pivotUrl, gateToken, err := GuestGateTokenGet(appType, abango.XConfig["DbuConnString"], abango.XConfig["DbuAppBase64"])
+	if err != nil {
+		return "", "", e.ErrLog(e.FuncRun("23rfsr3qrase", e.CurrFuncName()), err)
+	}
+
+	ret := &struct {
+		EncrypteKey string
+		SaltKey     string
+	}{}
+
+	bodyBytes, _ := json.Marshal(req)
+	apiUrl := pivotUrl + "/encrypt-get"
+	msgBytes, staInt, err := HttpResponseWithGt("POST", apiUrl, bodyBytes, gateToken)
+	if err != nil {
+		return "", "", e.ErrLog(e.FuncRun("1eadwrq34dxc-The HTTP request "+apiUrl, e.CurrFuncName()), err)
+	}
+	if staInt != 200 {
+		gAppApis[appType].GateToken = "" // GateToke Expired 경우 Clear 한다.
+		return "", "", errors.New(e.FuncRun("45faw3rfw-Request Fail "+string(msgBytes), e.CurrFuncName()))
+	}
+
+	if err := json.Unmarshal(msgBytes, ret); err != nil {
+		return "", "", e.ErrLog(e.FuncRun("6756er345r3", e.CurrFuncName()), err)
+	}
+
+	return ret.EncrypteKey, ret.SaltKey, nil
+}
+
+func GuestAvailDbupdateGet(lastno string, isskipup string) ([]byte, error) {
+
+	appType := 1 //Dbupdate
+	req := &struct {
+		DbupdateNo   string
+		IsSkipUpdate string
+	}{
+		DbupdateNo:   lastno,
+		IsSkipUpdate: isskipup,
+	}
+
+	// 0:Sso, 1:Dbu
+	pivotUrl, gateToken, err := GuestGateTokenGet(appType, abango.XConfig["DbuConnString"], abango.XConfig["DbuAppBase64"])
+	if err != nil {
+		return nil, e.ErrLog(e.FuncRun("23rfsr3qrase", e.CurrFuncName()), err)
+	}
+
+	bodyBytes, _ := json.Marshal(req)
+	apiUrl := pivotUrl + "/avail-dbupdate-get"
+	msgBytes, staInt, err := HttpResponseWithGt("POST", apiUrl, bodyBytes, gateToken)
+	if err != nil {
+		return nil, e.ErrLog(e.FuncRun("1eadwrq34dxc-The HTTP request "+apiUrl, e.CurrFuncName()), err)
+	}
+	if staInt != 200 {
+		gAppApis[appType].GateToken = "" // GateToke Expired 경우 Clear 한다.
+		return nil, errors.New(e.FuncRun("0asfweijcvs-Request Fail "+string(msgBytes), e.CurrFuncName()))
+	}
+	return msgBytes, nil
+}
+
+func GuestKeyPairGet(clientId string) (string, error) {
+	appType := 0 //Dbupdate
+	req := &struct {
+		ClientId string
+	}{
+		ClientId: clientId,
+	}
+	// fmt.Println("clientId:", clientId)
+	// 0:Sso, 1:Dbu
+	pivotUrl, gateToken, err := GuestGateTokenGet(appType, abango.XConfig["SsoConnString"], abango.XConfig["SsoAppBase64"])
+	if err != nil {
+		return "", e.ErrLog(e.FuncRun("23rfsr3qrase", e.CurrFuncName()), err)
+	}
+
+	ret := &struct {
+		KeyPair string
+	}{}
+
+	bodyBytes, _ := json.Marshal(req)
+	apiUrl := pivotUrl + "/key-pair-get"
+	msgBytes, staInt, err := HttpResponseWithGt("POST", apiUrl, bodyBytes, gateToken)
+	if err != nil {
+		return "", e.ErrLog(e.FuncRun("1eadwrq34dxc-The HTTP request "+apiUrl, e.CurrFuncName()), err)
+	}
+	if staInt != 200 {
+		gAppApis[appType].GateToken = "" // GateToke Expired 경우 Clear 한다.
+		return "", errors.New(e.FuncRun("09665gsre3-Request Fail "+string(msgBytes), e.CurrFuncName()))
+	}
+
+	if err := json.Unmarshal(msgBytes, ret); err != nil {
+		return "", e.ErrLog(e.FuncRun("9074tf32de", e.CurrFuncName()), err)
+	}
+
+	return ret.KeyPair, nil
+}
+
+func OneRowQuery(y *abango.Controller, sql string) (c1 string, c2 string, c3 string, err error) {
+	page, err := y.Db.Query(sql)
+	if err != nil {
+		return "", "", "", errors.New(e.FuncRunErr("0hjnboisqow", e.CurrFuncName()+err.Error()))
+	}
+	if len(page) > 1 {
+		return "", "", "", errors.New(e.FuncRunErr("0k1dt6j3d", e.CurrFuncName()+"Row Count > 1 "))
+	}
+
+	for _, row := range page {
+		c1 = string(row["c1"])
+		c2 = string(row["c2"])
+		c3 = string(row["c3"])
+	}
+	return
+}
+
+func IsFirstOrderGet(y *abango.Controller, buyerId int) string {
+	qry := fmt.Sprintf("select count(*) as c1 from dbr_sorder where buyer_id = %d ", buyerId)
+	ordCnt, _, _, _ := OneRowQuery(y, qry)
+	if ordCnt == "1" {
+		return "1"
+	} else {
+		return "0"
+	}
+}
+
+func TimeFormatGet(format string) string {
+	rtn := ""
+	if format == "" {
+		rtn = "060102"
+	} else if format == "YYMMDD" {
+		rtn = "060102"
+	} else if format == "YYYYMMDD" {
+		rtn = "20060102"
+	} else if format == "YY-MM-DD" {
+		rtn = "06-01-02"
+	} else if format == "YY.MM.DD" {
+		rtn = "06.01.02"
+	} else if format == "YYMM" {
+		rtn = "0601"
+	} else if format == "YY" {
+		rtn = "06"
+	}
+	return rtn
+}

+ 3 - 0
locals/common-func.go

@@ -1,3 +1,6 @@
+//go:build !dev
+// +build !dev
+
 package locals
 
 import (

+ 4 - 1
locals/gosodium/cryptobox/crypto_box.go

@@ -1,3 +1,6 @@
+//go:build !dev
+// +build !dev
+
 package cryptobox
 
 // #cgo pkg-config: libsodium
@@ -6,7 +9,7 @@ package cryptobox
 import "C"
 import (
 	"errors"
-	support "kkscrap-go/locals/gosodium/support"
+	support "syncscan-go/locals/gosodium/support"
 )
 
 func CryptoBoxSeedBytes() int {

+ 69 - 0
locals/gosodium/cryptobox/crypto_box_dev.go

@@ -0,0 +1,69 @@
+package cryptobox
+
+func CryptoBoxSeedBytes() int {
+	return 0
+}
+
+func CryptoBoxPublicKeyBytes() int {
+	return 0
+}
+
+func CryptoBoxSecretKeyBytes() int {
+	return 0
+}
+
+func CryptoBoxNonceBytes() int {
+	return 0
+}
+
+func CryptoBoxMacBytes() int {
+	return 0
+}
+
+func CryptoBoxPrimitive() string {
+	return ""
+}
+
+func CryptoBoxBeforeNmBytes() int {
+	return 0
+}
+
+func CryptoBoxZeroBytes() int {
+	return 0
+}
+
+func CryptoBoxBoxZeroBytes() int {
+	return 0
+}
+
+func CryptoBoxSeedKeyPair(seed []byte) ([]byte, []byte, int) {
+	return nil, nil, 0
+}
+
+func CryptoBoxKeyPair() ([]byte, []byte, int) {
+	return nil, nil, 0
+}
+
+func CryptoBoxBeforeNm(pk []byte, sk []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBox(m []byte, n []byte, pk []byte, sk []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxOpen(c []byte, n []byte, pk []byte, sk []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxAfterNm(m []byte, n []byte, k []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxOpenAfterNm(c []byte, n []byte, k []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxGetSecretPublicKeyFrom(keypair []byte) (sk, pk []byte, err error) {
+	return
+}

+ 4 - 1
locals/gosodium/cryptobox/crypto_box_easy.go

@@ -1,10 +1,13 @@
+//go:build !dev
+// +build !dev
+
 package cryptobox
 
 // #cgo pkg-config: libsodium
 // #include <stdlib.h>
 // #include <sodium.h>
 import "C"
-import support "kkscrap-go/locals/gosodium/support"
+import support "syncscan-go/locals/gosodium/support"
 
 func CryptoBoxDetachedAfterNm(mac []byte, m []byte, n []byte, k []byte) ([]byte, int) {
 	support.CheckSize(mac, CryptoBoxMacBytes(), "mac")

+ 33 - 0
locals/gosodium/cryptobox/crypto_box_easy_dev.go

@@ -0,0 +1,33 @@
+package cryptobox
+
+func CryptoBoxDetachedAfterNm(mac []byte, m []byte, n []byte, k []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxDetached(mac []byte, m []byte, n []byte, pk []byte, sk []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxEasyAfterNm(m []byte, n []byte, k []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxEasy(m []byte, n []byte, pk []byte, sk []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxOpenDetachedAfterNm(c []byte, mac []byte, n []byte, k []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxOpenDetached(c []byte, mac []byte, n []byte, pk []byte, sk []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxOpenEasyAfterNm(c []byte, n []byte, k []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxOpenEasy(c []byte, n []byte, pk []byte, sk []byte) ([]byte, int) {
+	return nil, 0
+}

+ 4 - 1
locals/gosodium/cryptobox/crypto_box_seal.go

@@ -1,10 +1,13 @@
+//go:build !dev
+// +build !dev
+
 package cryptobox
 
 // #cgo pkg-config: libsodium
 // #include <stdlib.h>
 // #include <sodium.h>
 import "C"
-import support "kkscrap-go/locals/gosodium/support"
+import support "syncscan-go/locals/gosodium/support"
 
 func CryptoBoxSeal(m []byte, pk []byte) ([]byte, int) {
 	support.CheckSize(pk, CryptoBoxPublicKeyBytes(), "public key")

+ 13 - 0
locals/gosodium/cryptobox/crypto_box_seal_dev.go

@@ -0,0 +1,13 @@
+package cryptobox
+
+func CryptoBoxSeal(m []byte, pk []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxSealOpen(c []byte, pk []byte, sk []byte) ([]byte, int) {
+	return nil, 0
+}
+
+func CryptoBoxSealBytes() int {
+	return 0
+}

+ 32 - 0
test/api.http

@@ -0,0 +1,32 @@
+POST http://localhost:19080/item-url-scrap
+Content-Type: application/json
+
+{
+  "itemUrl": "https://www.mangokuro.com/shop/turkey/"
+}
+###
+POST http://localhost:19080/item-url-scrap
+Content-Type: application/json
+
+{
+  "itemUrl": "https://www.iroirotokyo.com/product/%eb%ac%b4%eb%a3%8c%eb%b0%b0%ec%86%a1-%ec%98%a4%eb%a6%ac%ed%9e%88%eb%a1%9c-%ea%b3%a4%ec%95%bd%ec%a0%a4%eb%a6%ac-%ed%8c%8c%ec%9a%b0%ec%b9%98-%ec%b9%bc%eb%a1%9c%eb%a6%ac%ec%a0%9c%eb%a1%9c%eb%b0%b1/"
+}
+###
+POST http://localhost:19080/solution-type-get
+Content-Type: application/json
+
+{
+  "url": "https://www.iroirotokyo.com/product/%eb%ac%b4%eb%a3%8c%eb%b0%b0%ec%86%a1-%ec%98%a4%eb%a6%ac%ed%9e%88%eb%a1%9c-%ea%b3%a4%ec%95%bd%ec%a0%a4%eb%a6%ac-%ed%8c%8c%ec%9a%b0%ec%b9%98-%ec%b9%bc%eb%a1%9c%eb%a6%ac%ec%a0%9c%eb%a1%9c%eb%b0%b1/"
+}
+###
+POST http://localhost:19080/product-page-get
+Content-Type: application/json
+
+{
+  "solutionType": "wordpress",
+  "themeType": "",
+  "products": [
+    {"uri": "https://www.mangokuro.com/shop/turkey/"},
+    {"uri": "https://www.iroirotokyo.com/product/%eb%ac%b4%eb%a3%8c%eb%b0%b0%ec%86%a1-%ec%98%a4%eb%a6%ac%ed%9e%88%eb%a1%9c-%ea%b3%a4%ec%95%bd%ec%a0%a4%eb%a6%ac-%ed%8c%8c%ec%9a%b0%ec%b9%98-%ec%b9%bc%eb%a1%9c%eb%a6%ac%ec%a0%9c%eb%a1%9c%eb%b0%b1/"}
+  ]
+}

+ 24 - 0
test/main.go

@@ -0,0 +1,24 @@
+package main
+
+import (
+	"fmt"
+	"regexp"
+)
+
+func main() {
+
+	html := `aaaaaaaa
+<link rel="stylesheet" href="https://example.com/wp-content/themes/my-theme/style.css" type="text/css" media="all">
+aaaaaaaaaaa`
+
+	re := regexp.MustCompile(`wp-content\/themes\/(.+?)\/`)
+	result := re.FindStringSubmatch(html)
+
+	if len(result) > 1 {
+		themeName := result[1]
+		fmt.Println(themeName)
+	} else {
+		fmt.Println("Theme name not found")
+	}
+
+}