wrapper.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. package wrapper
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "html/template"
  8. "net/http"
  9. "os"
  10. "path/filepath"
  11. "strconv"
  12. "strings"
  13. "time"
  14. "golang-fave/cblocks"
  15. "golang-fave/consts"
  16. "golang-fave/engine/basket"
  17. "golang-fave/engine/mysqlpool"
  18. "golang-fave/engine/sqlw"
  19. "golang-fave/engine/wrapper/config"
  20. "golang-fave/logger"
  21. "golang-fave/utils"
  22. "github.com/vladimirok5959/golang-server-sessions/session"
  23. )
  24. type Tx = sqlw.Tx
  25. var ErrNoRows = sqlw.ErrNoRows
  26. type Wrapper struct {
  27. l *logger.Logger
  28. W http.ResponseWriter
  29. R *http.Request
  30. S *session.Session
  31. c *cblocks.CacheBlocks
  32. Host string
  33. Port string
  34. CurrHost string
  35. DConfig string
  36. DHtdocs string
  37. DLogs string
  38. DTemplate string
  39. DTmp string
  40. IsBackend bool
  41. ConfMysqlExists bool
  42. UrlArgs []string
  43. CurrModule string
  44. CurrSubModule string
  45. MSPool *mysqlpool.MySqlPool
  46. ShopBasket *basket.Basket
  47. Config *config.Config
  48. DB *sqlw.DB
  49. User *utils.MySql_user
  50. ShopAllCurrencies *map[int]utils.MySql_shop_currency
  51. }
  52. func New(l *logger.Logger, w http.ResponseWriter, r *http.Request, s *session.Session, c *cblocks.CacheBlocks, host, port, chost, dirConfig, dirHtdocs, dirLogs, dirTemplate, dirTmp string, mp *mysqlpool.MySqlPool, sb *basket.Basket) *Wrapper {
  53. conf := config.ConfigNew()
  54. if err := conf.ConfigRead(dirConfig + string(os.PathSeparator) + "config.json"); err != nil {
  55. l.Log("Host config file: %s", r, true, err.Error())
  56. }
  57. return &Wrapper{
  58. l: l,
  59. W: w,
  60. R: r,
  61. S: s,
  62. c: c,
  63. Host: host,
  64. Port: port,
  65. CurrHost: chost,
  66. DConfig: dirConfig,
  67. DHtdocs: dirHtdocs,
  68. DLogs: dirLogs,
  69. DTemplate: dirTemplate,
  70. DTmp: dirTmp,
  71. UrlArgs: []string{},
  72. CurrModule: "",
  73. CurrSubModule: "",
  74. MSPool: mp,
  75. ShopBasket: sb,
  76. Config: conf,
  77. }
  78. }
  79. func (this *Wrapper) LogAccess(msg string, vars ...interface{}) {
  80. this.l.Log(msg, this.R, false, vars...)
  81. }
  82. func (this *Wrapper) LogError(msg string, vars ...interface{}) {
  83. this.l.Log(msg, this.R, true, vars...)
  84. }
  85. func (this *Wrapper) LogCpError(err *error) *error {
  86. if *err != nil {
  87. this.LogError("%s", *err)
  88. }
  89. return err
  90. }
  91. func (this *Wrapper) dbReconnect() error {
  92. if !utils.IsMySqlConfigExists(this.DConfig + string(os.PathSeparator) + "mysql.json") {
  93. return errors.New("can't read database configuration file")
  94. }
  95. mc, err := utils.MySqlConfigRead(this.DConfig + string(os.PathSeparator) + "mysql.json")
  96. if err != nil {
  97. return err
  98. }
  99. this.DB, err = sqlw.Open("mysql", mc.User+":"+mc.Password+"@tcp("+mc.Host+":"+mc.Port+")/"+mc.Name)
  100. if err != nil {
  101. return err
  102. }
  103. this.MSPool.Set(this.CurrHost, this.DB)
  104. return nil
  105. }
  106. func (this *Wrapper) UseDatabase(ctx context.Context) error {
  107. this.DB = this.MSPool.Get(this.CurrHost)
  108. if this.DB == nil {
  109. if err := this.dbReconnect(); err != nil {
  110. return err
  111. }
  112. }
  113. if err := this.DB.Ping(ctx); err != nil {
  114. this.DB.Close()
  115. if err := this.dbReconnect(); err != nil {
  116. return err
  117. }
  118. if err := this.DB.Ping(ctx); err != nil {
  119. this.DB.Close()
  120. return err
  121. }
  122. }
  123. // Max 60 minutes and max 4 connection per host
  124. this.DB.SetConnMaxLifetime(time.Minute * 60)
  125. this.DB.SetMaxIdleConns(4)
  126. this.DB.SetMaxOpenConns(4)
  127. return nil
  128. }
  129. func (this *Wrapper) LoadSessionUser() bool {
  130. if this.S.GetInt("UserId", 0) <= 0 {
  131. return false
  132. }
  133. if this.DB == nil {
  134. return false
  135. }
  136. user := &utils.MySql_user{}
  137. err := this.DB.QueryRow(`
  138. SELECT
  139. id,
  140. first_name,
  141. last_name,
  142. email,
  143. password,
  144. admin,
  145. active
  146. FROM
  147. users
  148. WHERE
  149. id = ?
  150. LIMIT 1;`,
  151. this.S.GetInt("UserId", 0),
  152. ).Scan(
  153. &user.A_id,
  154. &user.A_first_name,
  155. &user.A_last_name,
  156. &user.A_email,
  157. &user.A_password,
  158. &user.A_admin,
  159. &user.A_active,
  160. )
  161. if *this.LogCpError(&err) != nil {
  162. return false
  163. }
  164. if user.A_id != this.S.GetInt("UserId", 0) {
  165. return false
  166. }
  167. this.User = user
  168. return true
  169. }
  170. func (this *Wrapper) Write(data string) {
  171. this.W.Write([]byte(data))
  172. }
  173. func (this *Wrapper) MsgSuccess(msg string) {
  174. this.Write(fmt.Sprintf(
  175. `fave.ShowMsgSuccess('Success!', '%s', false);`,
  176. utils.JavaScriptVarValue(msg)))
  177. }
  178. func (this *Wrapper) MsgError(msg string) {
  179. this.Write(fmt.Sprintf(
  180. `fave.ShowMsgError('Error!', '%s', true);`,
  181. utils.JavaScriptVarValue(msg)))
  182. }
  183. func (this *Wrapper) RenderToString(tcont []byte, data interface{}) string {
  184. tmpl, err := template.New("template").Parse(string(tcont))
  185. if err != nil {
  186. return err.Error()
  187. }
  188. var tpl bytes.Buffer
  189. if err := tmpl.Execute(&tpl, data); err != nil {
  190. return err.Error()
  191. }
  192. return tpl.String()
  193. }
  194. func (this *Wrapper) RenderFrontEnd(tname string, data interface{}, status int) {
  195. tmpl, err := template.New(tname+".html").Funcs(utils.TemplateAdditionalFuncs()).ParseFiles(
  196. this.DTemplate+string(os.PathSeparator)+tname+".html",
  197. this.DTemplate+string(os.PathSeparator)+"header.html",
  198. this.DTemplate+string(os.PathSeparator)+"sidebar-left.html",
  199. this.DTemplate+string(os.PathSeparator)+"sidebar-right.html",
  200. this.DTemplate+string(os.PathSeparator)+"footer.html",
  201. )
  202. if err != nil {
  203. utils.SystemErrorPageTemplate(this.W, err)
  204. return
  205. }
  206. var tpl bytes.Buffer
  207. err = tmpl.Execute(&tpl, consts.TmplData{
  208. System: utils.GetTmplSystemData("", ""),
  209. Data: data,
  210. })
  211. if err != nil {
  212. utils.SystemErrorPageTemplate(this.W, err)
  213. return
  214. }
  215. this.W.WriteHeader(status)
  216. this.W.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
  217. this.W.Header().Set("Content-Type", "text/html; charset=utf-8")
  218. this.W.Write(tpl.Bytes())
  219. }
  220. func (this *Wrapper) RenderBackEnd(tcont []byte, data interface{}) {
  221. tmpl, err := template.New("template").Parse(string(tcont))
  222. if err != nil {
  223. utils.SystemErrorPageEngine(this.W, err)
  224. return
  225. }
  226. this.W.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
  227. this.W.Header().Set("Content-Type", "text/html; charset=utf-8")
  228. var tpl bytes.Buffer
  229. err = tmpl.Execute(this.W, consts.TmplData{
  230. System: utils.GetTmplSystemData(this.CurrModule, this.CurrSubModule),
  231. Data: data,
  232. })
  233. if err != nil {
  234. utils.SystemErrorPageEngine(this.W, err)
  235. return
  236. }
  237. this.W.Write(tpl.Bytes())
  238. }
  239. func (this *Wrapper) GetCurrentPage(max int) int {
  240. curr := 1
  241. page := this.R.URL.Query().Get("p")
  242. if page != "" {
  243. if i, err := strconv.Atoi(page); err == nil {
  244. if i < 1 {
  245. curr = 1
  246. } else if i > max {
  247. curr = max
  248. } else {
  249. curr = i
  250. }
  251. }
  252. }
  253. return curr
  254. }
  255. func (this *Wrapper) ConfigSave() error {
  256. return this.Config.ConfigWrite(this.DConfig + string(os.PathSeparator) + "config.json")
  257. }
  258. func (this *Wrapper) SendEmailUsual(email, subject, message string) error {
  259. if (*this.Config).SMTP.Host == "" || (*this.Config).SMTP.Login == "" || (*this.Config).SMTP.Password == "" {
  260. return errors.New("SMTP server is not configured")
  261. }
  262. err := utils.SMTPSend(
  263. (*this.Config).SMTP.Host,
  264. utils.IntToStr((*this.Config).SMTP.Port),
  265. (*this.Config).SMTP.Login,
  266. (*this.Config).SMTP.Password,
  267. subject,
  268. message,
  269. []string{email},
  270. )
  271. status := 1
  272. emessage := ""
  273. if err != nil {
  274. status = 0
  275. emessage = err.Error()
  276. }
  277. if _, err := this.DB.Exec(
  278. `INSERT INTO notify_mail SET
  279. id = NULL,
  280. email = ?,
  281. subject = ?,
  282. message = ?,
  283. error = ?,
  284. datetime = ?,
  285. status = ?
  286. ;`,
  287. email,
  288. subject,
  289. message,
  290. emessage,
  291. utils.UnixTimestampToMySqlDateTime(utils.GetCurrentUnixTimestamp()),
  292. status,
  293. ); err != nil {
  294. this.LogCpError(&err)
  295. }
  296. return err
  297. }
  298. func (this *Wrapper) SendEmailFast(email, subject, message string) error {
  299. status := 2
  300. emessage := ""
  301. if (*this.Config).SMTP.Host == "" || (*this.Config).SMTP.Login == "" || (*this.Config).SMTP.Password == "" {
  302. status = 0
  303. emessage = "SMTP server is not configured"
  304. }
  305. if _, err := this.DB.Exec(
  306. `INSERT INTO notify_mail SET
  307. id = NULL,
  308. email = ?,
  309. subject = ?,
  310. message = ?,
  311. error = ?,
  312. datetime = ?,
  313. status = ?
  314. ;`,
  315. email,
  316. subject,
  317. message,
  318. emessage,
  319. utils.UnixTimestampToMySqlDateTime(utils.GetCurrentUnixTimestamp()),
  320. status,
  321. ); err != nil {
  322. return err
  323. }
  324. return nil
  325. }
  326. func (this *Wrapper) SendEmailTemplated(email, subject, tname string, data interface{}) error {
  327. tmpl, err := template.New(tname + ".html").Funcs(utils.TemplateAdditionalFuncs()).ParseFiles(
  328. this.DTemplate + string(os.PathSeparator) + tname + ".html",
  329. )
  330. if err != nil {
  331. return err
  332. }
  333. var tpl bytes.Buffer
  334. err = tmpl.Execute(&tpl, data)
  335. if err != nil {
  336. return err
  337. }
  338. return this.SendEmailFast(email, subject, string(tpl.Bytes()))
  339. }
  340. func (this *Wrapper) GetSessionId() string {
  341. cookie, err := this.R.Cookie("session")
  342. if err == nil && len(cookie.Value) == 40 {
  343. return cookie.Value
  344. }
  345. return ""
  346. }
  347. func (this *Wrapper) RecreateProductXmlFile() error {
  348. trigger := strings.Join([]string{this.DTmp, "trigger.xml.run"}, string(os.PathSeparator))
  349. if !utils.IsFileExists(trigger) {
  350. if _, err := os.Create(trigger); err != nil {
  351. return err
  352. }
  353. }
  354. return nil
  355. }
  356. func (this *Wrapper) RecreateProductImgFiles() error {
  357. trigger := strings.Join([]string{this.DTmp, "trigger.img.run"}, string(os.PathSeparator))
  358. if !utils.IsFileExists(trigger) {
  359. if _, err := os.Create(trigger); err != nil {
  360. return err
  361. }
  362. }
  363. return nil
  364. }
  365. func (this *Wrapper) RemoveProductImageThumbnails(product_id, filename string) error {
  366. pattern := this.DHtdocs + string(os.PathSeparator) + strings.Join([]string{"products", "images", product_id, filename}, string(os.PathSeparator))
  367. if files, err := filepath.Glob(pattern); err != nil {
  368. return err
  369. } else {
  370. for _, file := range files {
  371. if err := os.Remove(file); err != nil {
  372. return errors.New(fmt.Sprintf("[upload delete] Thumbnail file (%s) delete error: %s", file, err.Error()))
  373. }
  374. }
  375. }
  376. return this.RecreateProductImgFiles()
  377. }
  378. func (this *Wrapper) ShopGetAllCurrencies() *map[int]utils.MySql_shop_currency {
  379. if this.ShopAllCurrencies == nil {
  380. this.ShopAllCurrencies = &map[int]utils.MySql_shop_currency{}
  381. if rows, err := this.DB.Query(
  382. `SELECT
  383. id,
  384. name,
  385. coefficient,
  386. code,
  387. symbol
  388. FROM
  389. shop_currencies
  390. ORDER BY
  391. id ASC
  392. ;`,
  393. ); err == nil {
  394. defer rows.Close()
  395. for rows.Next() {
  396. row := utils.MySql_shop_currency{}
  397. if err = rows.Scan(
  398. &row.A_id,
  399. &row.A_name,
  400. &row.A_coefficient,
  401. &row.A_code,
  402. &row.A_symbol,
  403. ); err == nil {
  404. (*this.ShopAllCurrencies)[row.A_id] = row
  405. }
  406. }
  407. }
  408. }
  409. return this.ShopAllCurrencies
  410. }
  411. func (this *Wrapper) ShopGetCurrentCurrency() *utils.MySql_shop_currency {
  412. currency_id := 1
  413. if cookie, err := this.R.Cookie("currency"); err == nil {
  414. currency_id = utils.StrToInt(cookie.Value)
  415. }
  416. if _, ok := (*this.ShopGetAllCurrencies())[currency_id]; ok != true {
  417. currency_id = 1
  418. }
  419. if p, ok := (*this.ShopGetAllCurrencies())[currency_id]; ok == true {
  420. return &p
  421. }
  422. return nil
  423. }