admin_controller.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. // Copyright (c) [2022] [巴拉迪维 BaratSemet]
  2. // [ohUrlShortener] is licensed under Mulan PSL v2.
  3. // You can use this software according to the terms and conditions of the Mulan PSL v2.
  4. // You may obtain a copy of Mulan PSL v2 at:
  5. // http://license.coscl.org.cn/MulanPSL2
  6. // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
  7. // See the Mulan PSL v2 for more details.
  8. package controller
  9. import (
  10. "fmt"
  11. "net/http"
  12. "ohurlshortener/core"
  13. "ohurlshortener/service"
  14. "ohurlshortener/utils"
  15. "ohurlshortener/utils/export"
  16. "strconv"
  17. "strings"
  18. "time"
  19. "github.com/dchest/captcha"
  20. "github.com/gin-gonic/gin"
  21. )
  22. const (
  23. DEFAULT_PAGE_NUM = 1
  24. DEFAULT_PAGE_SIZE = 20
  25. )
  26. func LoginPage(c *gin.Context) {
  27. c.HTML(http.StatusOK, "login.html", gin.H{
  28. "title": "登录 - ohUrlShortener",
  29. })
  30. }
  31. func DoLogin(c *gin.Context) {
  32. account := c.PostForm("account")
  33. password := c.PostForm("password")
  34. captchaText := c.PostForm("captcha-text")
  35. captchaId := c.PostForm("captcha-id")
  36. if utils.EmptyString(account) || utils.EmptyString(password) || len(account) < 5 || len(password) < 8 {
  37. c.HTML(http.StatusOK, "login.html", gin.H{
  38. "title": "错误 - ohUrlShortener",
  39. "error": "用户名或密码格式错误!",
  40. })
  41. return
  42. }
  43. if utils.EmptyString(captchaText) || utils.EmptyString(captchaId) || len(captchaText) < 6 {
  44. c.HTML(http.StatusOK, "login.html", gin.H{
  45. "title": "错误 - ohUrlShortener",
  46. "error": "验证码格式错误!",
  47. })
  48. return
  49. }
  50. //验证码有效性验证
  51. if !captcha.VerifyString(captchaId, captchaText) {
  52. c.HTML(http.StatusOK, "login.html", gin.H{
  53. "title": "错误 - ohUrlShortener",
  54. "error": "验证码错误,请刷新页面再重新尝试!",
  55. })
  56. return
  57. }
  58. //用户名密码有效性验证
  59. loginUser, err := service.Login(account, password)
  60. if err != nil || loginUser.IsEmpty() {
  61. c.HTML(http.StatusOK, "login.html", gin.H{
  62. "title": "错误 - ohUrlShortener",
  63. "error": err.Error(),
  64. })
  65. return
  66. }
  67. //Write Cookie to browser
  68. cValue, err := AdminCookieValue(loginUser)
  69. if err != nil {
  70. c.HTML(http.StatusOK, "login.html", gin.H{
  71. "title": "错误 - ohUrlShortener",
  72. "error": "内部错误,请联系管理员",
  73. })
  74. return
  75. }
  76. c.SetCookie("ohUrlShortenerAdmin", loginUser.Account, 3600, "/", "", false, true)
  77. c.SetCookie("ohUrlShortenerCookie", cValue, 3600, "/", "", false, true)
  78. c.Redirect(http.StatusFound, "/admin/dashboard")
  79. }
  80. func DoLogout(c *gin.Context) {
  81. c.SetCookie("ohUrlShortenerAdmin", "", -1, "/", "", false, true)
  82. c.SetCookie("ohUrlShortenerCookie", "", -1, "/", "", false, true)
  83. c.Redirect(http.StatusFound, "/login")
  84. }
  85. func ServeCaptchaImage(c *gin.Context) {
  86. captcha.Server(200, 45).ServeHTTP(c.Writer, c.Request)
  87. }
  88. func RequestCaptchaImage(c *gin.Context) {
  89. imageId := captcha.New()
  90. c.JSON(http.StatusOK, core.ResultJsonSuccessWithData(imageId))
  91. }
  92. func ChangeState(c *gin.Context) {
  93. destUrl := c.PostForm("dest_url")
  94. enable := c.PostForm("enable")
  95. if utils.EmptyString(destUrl) {
  96. c.JSON(http.StatusBadRequest, core.ResultJsonError("目标链接不存在!"))
  97. return
  98. }
  99. destEnable, err := strconv.ParseBool(enable)
  100. if err != nil {
  101. c.JSON(http.StatusBadRequest, core.ResultJsonError("参数不合法!"))
  102. return
  103. }
  104. result, er := service.ChangeState(destUrl, destEnable)
  105. if er != nil {
  106. c.JSON(http.StatusInternalServerError, core.ResultJsonError(er.Error()))
  107. return
  108. }
  109. c.JSON(http.StatusOK, core.ResultJsonSuccessWithData(result))
  110. }
  111. func DeleteShortUrl(c *gin.Context) {
  112. url := c.PostForm("short_url")
  113. if utils.EmptyString(strings.TrimSpace(url)) {
  114. c.JSON(http.StatusBadRequest, core.ResultJsonError("目标链接不存在!"))
  115. return
  116. }
  117. err := service.DeleteUrlAndAccessLogs(url)
  118. if err != nil {
  119. c.JSON(http.StatusInternalServerError, core.ResultJsonError(err.Error()))
  120. return
  121. }
  122. c.JSON(http.StatusOK, core.ResultJsonSuccess())
  123. }
  124. func GenerateShortUrl(c *gin.Context) {
  125. destUrl := c.PostForm("dest_url")
  126. memo := c.PostForm("memo")
  127. if utils.EmptyString(destUrl) {
  128. c.JSON(http.StatusBadRequest, core.ResultJsonError("目标链接不能为空!"))
  129. return
  130. }
  131. result, err := service.GenerateShortUrl(destUrl, memo)
  132. if err != nil {
  133. c.JSON(http.StatusInternalServerError, core.ResultJsonError(err.Error()))
  134. return
  135. }
  136. json := map[string]string{
  137. "short_url": fmt.Sprintf("%s%s", utils.AppConfig.UrlPrefix, result),
  138. }
  139. c.JSON(http.StatusOK, core.ResultJsonSuccessWithData(json))
  140. }
  141. func StatsPage(c *gin.Context) {
  142. url := c.DefaultQuery("url", "")
  143. strPage := c.DefaultQuery("page", strconv.Itoa(DEFAULT_PAGE_NUM))
  144. strSize := c.DefaultQuery("size", strconv.Itoa(DEFAULT_PAGE_SIZE))
  145. page, err := strconv.Atoi(strPage)
  146. if err != nil {
  147. page = DEFAULT_PAGE_NUM
  148. }
  149. size, err := strconv.Atoi(strSize)
  150. if err != nil {
  151. size = DEFAULT_PAGE_SIZE
  152. }
  153. urls, err := service.GetPagedUrlIpCountStats(strings.TrimSpace(url), page, size)
  154. c.HTML(http.StatusOK, "stats.html", gin.H{
  155. "title": "数据统计 - ohUrlShortener",
  156. "current_url": c.Request.URL.Path,
  157. "error": err,
  158. "shortUrls": urls,
  159. "page": page,
  160. "size": size,
  161. "prefix": utils.AppConfig.UrlPrefix,
  162. "first_page": page == 1,
  163. "last_page": len(urls) < size,
  164. "url": strings.TrimSpace(url),
  165. })
  166. }
  167. func SearchStatsPage(c *gin.Context) {
  168. url := c.DefaultQuery("url", "")
  169. strPage := c.DefaultQuery("page", strconv.Itoa(DEFAULT_PAGE_NUM))
  170. strSize := c.DefaultQuery("size", strconv.Itoa(DEFAULT_PAGE_SIZE))
  171. page, err := strconv.Atoi(strPage)
  172. if err != nil {
  173. page = DEFAULT_PAGE_NUM
  174. }
  175. size, err := strconv.Atoi(strSize)
  176. if err != nil {
  177. size = DEFAULT_PAGE_SIZE
  178. }
  179. urls, err := service.GetPagedUrlIpCountStats(strings.TrimSpace(url), page, size)
  180. c.HTML(http.StatusOK, "search_stats.html", gin.H{
  181. "title": "查询统计 - ohUrlShortener",
  182. "current_url": c.Request.URL.Path,
  183. "error": err,
  184. "shortUrls": urls,
  185. "page": page,
  186. "size": size,
  187. "prefix": utils.AppConfig.UrlPrefix,
  188. "first_page": page == 1,
  189. "last_page": len(urls) < size,
  190. "url": strings.TrimSpace(url),
  191. })
  192. }
  193. func UrlsPage(c *gin.Context) {
  194. url := c.DefaultQuery("url", "")
  195. strPage := c.DefaultQuery("page", strconv.Itoa(DEFAULT_PAGE_NUM))
  196. strSize := c.DefaultQuery("size", strconv.Itoa(DEFAULT_PAGE_SIZE))
  197. page, err := strconv.Atoi(strPage)
  198. if err != nil {
  199. page = DEFAULT_PAGE_NUM
  200. }
  201. size, err := strconv.Atoi(strSize)
  202. if err != nil {
  203. size = DEFAULT_PAGE_SIZE
  204. }
  205. urls, err := service.GetPagesShortUrls(strings.TrimSpace(url), page, size)
  206. c.HTML(http.StatusOK, "urls.html", gin.H{
  207. "title": "短链接列表 - ohUrlShortener",
  208. "current_url": c.Request.URL.Path,
  209. "error": err,
  210. "shortUrls": urls,
  211. "page": page,
  212. "size": size,
  213. "prefix": utils.AppConfig.UrlPrefix,
  214. "first_page": page == 1,
  215. "last_page": len(urls) < size,
  216. "url": strings.TrimSpace(url),
  217. })
  218. }
  219. func AccessLogsPage(c *gin.Context) {
  220. url := c.DefaultQuery("url", "")
  221. strPage := c.DefaultQuery("page", strconv.Itoa(DEFAULT_PAGE_NUM))
  222. strSize := c.DefaultQuery("size", strconv.Itoa(DEFAULT_PAGE_SIZE))
  223. start := c.DefaultQuery("start", "")
  224. end := c.DefaultQuery("end", "")
  225. page, err := strconv.Atoi(strPage)
  226. if err != nil {
  227. page = DEFAULT_PAGE_NUM
  228. }
  229. size, err := strconv.Atoi(strSize)
  230. if err != nil {
  231. size = DEFAULT_PAGE_SIZE
  232. }
  233. totalCount, distinctIpCount, err := service.GetAccessLogsCount(strings.TrimSpace(url), start, end)
  234. logs, err := service.GetPagedAccessLogs(strings.TrimSpace(url), start, end, page, size)
  235. c.HTML(http.StatusOK, "access_logs.html", gin.H{
  236. "title": "访问日志查询 - ohUrlShortener",
  237. "current_url": c.Request.URL.Path,
  238. "error": err,
  239. "logs": logs,
  240. "page": page,
  241. "size": size,
  242. "prefix": utils.AppConfig.UrlPrefix,
  243. "first_page": page == 1,
  244. "last_page": len(logs) < size,
  245. "url": strings.TrimSpace(url),
  246. "total_count": totalCount,
  247. "unique_ip_count": distinctIpCount,
  248. "start_date": start,
  249. "end_date": end,
  250. })
  251. }
  252. func AccessLogsExport(c *gin.Context) {
  253. url := c.PostForm("url")
  254. logs, err := service.GetAllAccessLogs(strings.TrimSpace(url))
  255. if err != nil {
  256. c.HTML(http.StatusOK, "access_logs.html", gin.H{
  257. "title": "访问日志查询 - ohUrlShortener",
  258. "current_url": c.Request.URL.Path,
  259. "prefix": utils.AppConfig.UrlPrefix,
  260. "url": strings.TrimSpace(url),
  261. "error": err,
  262. })
  263. return
  264. }
  265. fileContent, err := export.AccessLogToExcel(logs)
  266. if err != nil {
  267. c.HTML(http.StatusOK, "access_logs.html", gin.H{
  268. "title": "访问日志查询 - ohUrlShortener",
  269. "current_url": c.Request.URL.Path,
  270. "prefix": utils.AppConfig.UrlPrefix,
  271. "url": strings.TrimSpace(url),
  272. "error": err,
  273. })
  274. return
  275. }
  276. attachmentName := "访问日志" + time.Now().Format("2006-01-02 150405") + ".xlsx"
  277. fileContentDisposition := "attachment;filename=\"" + attachmentName + "\""
  278. c.Header("Content-Disposition", fileContentDisposition)
  279. c.Data(http.StatusOK, "pplication/octet-stream", fileContent)
  280. }
  281. func DashboardPage(c *gin.Context) {
  282. count, stats, err := service.GetSumOfUrlStats()
  283. if err != nil {
  284. c.HTML(http.StatusOK, "dashboard.html", gin.H{
  285. "title": "仪表盘 - ohUrlShortener",
  286. "current_url": c.Request.URL.Path,
  287. "error": err,
  288. })
  289. return
  290. }
  291. top25, er := service.GetTop25Url()
  292. if er != nil {
  293. c.HTML(http.StatusOK, "dashboard.html", gin.H{
  294. "title": "仪表盘 - ohUrlShortener",
  295. "current_url": c.Request.URL.Path,
  296. "error": er,
  297. })
  298. return
  299. }
  300. c.HTML(http.StatusOK, "dashboard.html", gin.H{
  301. "title": "仪表盘 - ohUrlShortener",
  302. "current_url": c.Request.URL.Path,
  303. "error": err,
  304. "total_count": count,
  305. "prefix": utils.AppConfig.UrlPrefix,
  306. "stats": stats,
  307. "top25": top25,
  308. })
  309. }