diff --git a/backend/__debug_bin3332691057 b/backend/__debug_bin3332691057
new file mode 100755
index 0000000..54cbb6d
Binary files /dev/null and b/backend/__debug_bin3332691057 differ
diff --git a/backend/modules/middlewares/m_wechat_auth.go b/backend/modules/middlewares/m_wechat_auth.go
new file mode 100644
index 0000000..ce46aaf
--- /dev/null
+++ b/backend/modules/middlewares/m_wechat_auth.go
@@ -0,0 +1,46 @@
+package middlewares
+
+import (
+ "strings"
+
+ "backend/providers/wechat"
+
+ "github.com/gofiber/fiber/v3"
+ "github.com/pkg/errors"
+ log "github.com/sirupsen/logrus"
+)
+
+func (f *Middlewares) WeChatAuth(c fiber.Ctx) error {
+ log.WithField("module", "middleware.AuthUserInfo").Debugf("%s, query: %v", c.OriginalURL(), c.Queries())
+ state := c.Query("state")
+ code := c.Query("code")
+ log.WithField("module", "middleware.AuthUserInfo").Debugf("code: %s, state: %s", code, state)
+
+ if state == "" && code == "" {
+ url := string(c.Request().URI().FullURI())
+ if f.app.IsDevMode() && f.app.BaseURI != nil {
+ url = strings.ReplaceAll(url, "http", "https")
+ url = strings.ReplaceAll(url, c.BaseURL(), *f.app.BaseURI)
+ }
+
+ log.WithField("module", "middleware.SilentAuth").Debug("redirect_uri: ", url)
+
+ to, err := f.client.ScopeAuthorizeURL(
+ wechat.ScopeAuthorizeURLWithRedirectURI(url),
+ wechat.ScopeAuthorizeURLWithState("sns_basic_auth"),
+ )
+ if err != nil {
+ return errors.Wrap(err, "failed to get wechat auth url")
+ }
+ log.WithField("module", "middleware.SilentAuth").Debug("redirectTo: ", to.String())
+
+ return c.Redirect().To(to.String())
+
+ }
+
+ if state != "sns_basic_auth" || code == "" {
+ return errors.New("invalid request")
+ }
+
+ return c.Next()
+}
diff --git a/backend/modules/middlewares/m_wechat_auth_userinfo.go b/backend/modules/middlewares/m_wechat_auth_userinfo.go
deleted file mode 100644
index af5b521..0000000
--- a/backend/modules/middlewares/m_wechat_auth_userinfo.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package middlewares
-
-import (
- _ "embed"
- "os"
- "path/filepath"
- "strings"
-
- "backend/pkg/pg"
- "backend/providers/jwt"
-
- "github.com/gofiber/fiber/v3"
- "github.com/jinzhu/copier"
- "github.com/pkg/errors"
- "github.com/samber/lo"
- log "github.com/sirupsen/logrus"
-)
-
-func (f *Middlewares) WeChatAuthUserInfo(c fiber.Ctx) error {
- // 如果请求存在 Authorization 头,则跳过
- if len(c.GetReqHeaders()["Authorization"]) != 0 {
- return c.Next()
- }
-
- log.WithField("module", "middleware.AuthUserInfo").Debugf("%s, query: %v", c.OriginalURL(), c.Queries())
- state := c.Query("state")
- code := c.Query("code")
-
- if state == "" && code == "" {
- return c.Next()
- }
-
- if state != "sns_basic_auth" {
- return c.Next()
- }
- log.WithField("module", "middleware.AuthUserInfo").Debugf("code: %s, state: %s", code, state)
-
- // get the openid
- token, err := f.client.AuthorizeCode2Token(code)
- if err != nil {
- return errors.Wrap(err, "failed to get openid")
- }
- log.Debugf("tokenInfo %+v", token)
-
- paths := lo.Filter(strings.Split(c.Path(), "/"), func(s string, _ int) bool {
- return s != ""
- })
- if len(paths) < 2 || paths[0] != "t" {
- return errors.New("invalid path")
- }
-
- tenantSlug := paths[1]
- if tenantSlug == "" {
- return errors.New("tenant is empty")
- }
-
- tenant, err := f.userSvc.GetTenantBySlug(c.Context(), tenantSlug)
- if err != nil {
- return errors.Wrap(err, "failed to get tenant id")
- }
-
- var oauthInfo pg.UserOAuth
- if err := copier.Copy(&oauthInfo, token); err != nil {
- return errors.Wrap(err, "failed to copy oauth info")
- }
- log.Debugf("oauthInfo %+v", oauthInfo)
-
- user, err := f.userSvc.GetOrNew(c.Context(), tenant.ID, token.Openid, oauthInfo)
- if err != nil {
- return errors.Wrap(err, "failed to get user")
- }
-
- claim := f.jwt.CreateClaims(jwt.BaseClaims{
- OpenID: user.OpenID,
- Tenant: tenantSlug,
- UserID: user.ID,
- TenantID: tenant.ID,
- })
- jwtToken, err := f.jwt.CreateToken(claim)
- if err != nil {
- return errors.Wrap(err, "failed to create token")
- }
-
- b, err := os.ReadFile(filepath.Join(f.storagePath.Asset, "index.html"))
- if err != nil {
- return errors.Wrap(err, "failed to read file")
- }
-
- html := strings.ReplaceAll(string(b), "{{JWT}}", jwtToken)
-
- c.Set("Content-Type", "text/html")
- return c.SendString(html)
-}
diff --git a/backend/modules/middlewares/m_wechat_silent_auth.go b/backend/modules/middlewares/m_wechat_silent_auth.go
deleted file mode 100644
index 4f47318..0000000
--- a/backend/modules/middlewares/m_wechat_silent_auth.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package middlewares
-
-import (
- "strings"
-
- "backend/providers/wechat"
-
- "github.com/gofiber/fiber/v3"
- "github.com/pkg/errors"
- log "github.com/sirupsen/logrus"
-)
-
-func (f *Middlewares) WeChatSilentAuth(c fiber.Ctx) error {
- // if cookie not exists key "openid", then redirect to the wechat auth page
- token := c.GetReqHeaders()["Authorization"]
- if len(token) != 0 {
- return c.Next()
- }
-
- // get current full url
- url := string(c.Request().URI().FullURI())
- if f.app.IsDevMode() && f.app.BaseURI != nil {
- url = strings.ReplaceAll(url, "http", "https")
- url = strings.ReplaceAll(url, c.BaseURL(), *f.app.BaseURI)
- }
-
- log.WithField("module", "middleware.SilentAuth").Debug("redirect_uri: ", url)
-
- to, err := f.client.ScopeAuthorizeURL(
- wechat.ScopeAuthorizeURLWithRedirectURI(url),
- wechat.ScopeAuthorizeURLWithState("sns_basic_auth"),
- )
- if err != nil {
- return errors.Wrap(err, "failed to get wechat auth url")
- }
- log.WithField("module", "middleware.SilentAuth").Debug("redirectTo: ", to.String())
-
- return c.Redirect().To(to.String())
-}
diff --git a/backend/modules/middlewares/mid_debug.go b/backend/modules/middlewares/mid_debug.go
index 92716b1..519442e 100644
--- a/backend/modules/middlewares/mid_debug.go
+++ b/backend/modules/middlewares/mid_debug.go
@@ -2,12 +2,14 @@ package middlewares
import (
"github.com/gofiber/fiber/v3"
+ log "github.com/sirupsen/logrus"
)
func (f *Middlewares) DebugMode(c fiber.Ctx) error {
// fullURI := c.Request().URI().FullURI()
// host := c.BaseURL()
// fmt.Println(strings.Split(c.Path(), "/"))
- // return c.SendString(c.Params("tenant", "no tenant: "+c.Path()))
+ // return c.SendString("ABC" + c.Params("+"))
+ log.SetLevel(log.DebugLevel)
return c.Next()
}
diff --git a/backend/modules/middlewares/provider.gen.go b/backend/modules/middlewares/provider.gen.go
index 58001f2..b1f58f6 100755
--- a/backend/modules/middlewares/provider.gen.go
+++ b/backend/modules/middlewares/provider.gen.go
@@ -4,6 +4,7 @@ import (
"backend/modules/users"
"backend/providers/app"
"backend/providers/jwt"
+ "backend/providers/storage"
"backend/providers/wechat"
"git.ipao.vip/rogeecn/atom/container"
@@ -15,13 +16,15 @@ func Provide(opts ...opt.Option) error {
app *app.Config,
client *wechat.Client,
jwt *jwt.JWT,
+ storagePath *storage.Config,
userSvc *users.Service,
) (*Middlewares, error) {
obj := &Middlewares{
- app: app,
- client: client,
- jwt: jwt,
- userSvc: userSvc,
+ app: app,
+ client: client,
+ jwt: jwt,
+ storagePath: storagePath,
+ userSvc: userSvc,
}
if err := obj.Prepare(); err != nil {
return nil, err
diff --git a/backend/modules/wechat/controller.go b/backend/modules/wechat/controller.go
new file mode 100644
index 0000000..f20db5a
--- /dev/null
+++ b/backend/modules/wechat/controller.go
@@ -0,0 +1,75 @@
+package users
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+
+ "backend/modules/users"
+ "backend/pkg/pg"
+ "backend/providers/jwt"
+ "backend/providers/storage"
+ "backend/providers/wechat"
+
+ "github.com/gofiber/fiber/v3"
+ "github.com/jinzhu/copier"
+ "github.com/pkg/errors"
+ log "github.com/sirupsen/logrus"
+)
+
+// @provider
+type Controller struct {
+ jwt *jwt.JWT
+ storagePath *storage.Config
+ userSvc *users.Service
+ client *wechat.Client
+}
+
+func (c *Controller) Render(ctx fiber.Ctx) error {
+ code := ctx.Query("code")
+
+ // get the openid
+ token, err := c.client.AuthorizeCode2Token(code)
+ if err != nil {
+ return errors.Wrap(err, "failed to get openid")
+ }
+ log.Debugf("tokenInfo %+v", token)
+
+ tenantSlug := ctx.Params("tenant")
+ tenant, err := c.userSvc.GetTenantBySlug(ctx.Context(), tenantSlug)
+ if err != nil {
+ return errors.Wrap(err, "failed to get tenant id")
+ }
+
+ var oauthInfo pg.UserOAuth
+ if err := copier.Copy(&oauthInfo, token); err != nil {
+ return errors.Wrap(err, "failed to copy oauth info")
+ }
+ log.Debugf("oauthInfo %+v", oauthInfo)
+
+ user, err := c.userSvc.GetOrNew(ctx.Context(), tenant.ID, token.Openid, oauthInfo)
+ if err != nil {
+ return errors.Wrap(err, "failed to get user")
+ }
+
+ claim := c.jwt.CreateClaims(jwt.BaseClaims{
+ OpenID: user.OpenID,
+ Tenant: tenantSlug,
+ UserID: user.ID,
+ TenantID: tenant.ID,
+ })
+ jwtToken, err := c.jwt.CreateToken(claim)
+ if err != nil {
+ return errors.Wrap(err, "failed to create token")
+ }
+
+ b, err := os.ReadFile(filepath.Join(c.storagePath.Asset, "index.html"))
+ if err != nil {
+ return errors.Wrap(err, "failed to read file")
+ }
+
+ html := strings.ReplaceAll(string(b), "{{JWT}}", jwtToken)
+
+ ctx.Set("Content-Type", "text/html")
+ return ctx.SendString(html)
+}
diff --git a/backend/modules/wechat/provider.gen.go b/backend/modules/wechat/provider.gen.go
new file mode 100755
index 0000000..2506977
--- /dev/null
+++ b/backend/modules/wechat/provider.gen.go
@@ -0,0 +1,32 @@
+package users
+
+import (
+ "backend/modules/users"
+ "backend/providers/jwt"
+ "backend/providers/storage"
+ "backend/providers/wechat"
+
+ "git.ipao.vip/rogeecn/atom/container"
+ "git.ipao.vip/rogeecn/atom/utils/opt"
+)
+
+func Provide(opts ...opt.Option) error {
+ if err := container.Container.Provide(func(
+ client *wechat.Client,
+ jwt *jwt.JWT,
+ storagePath *storage.Config,
+ userSvc *users.Service,
+ ) (*Controller, error) {
+ obj := &Controller{
+ client: client,
+ jwt: jwt,
+ storagePath: storagePath,
+ userSvc: userSvc,
+ }
+ return obj, nil
+ }); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/backend/pkg/service/http/http.go b/backend/pkg/service/http/http.go
index 3e79972..6cea40a 100644
--- a/backend/pkg/service/http/http.go
+++ b/backend/pkg/service/http/http.go
@@ -6,6 +6,7 @@ import (
"backend/modules/medias"
"backend/modules/middlewares"
"backend/modules/users"
+ wechatModule "backend/modules/wechat"
"backend/providers/app"
"backend/providers/hashids"
"backend/providers/http"
@@ -46,6 +47,7 @@ func Command() atom.Option {
middlewares.Provide,
users.Provide,
medias.Provide,
+ wechatModule.Provide,
)),
)
}
@@ -55,6 +57,7 @@ type Http struct {
App *app.Config
Storage *storage.Config
+ Wechat *wechatModule.Controller
Service *http.Service
Initials []contracts.Initial `group:"initials"`
Routes []contracts.HttpRoute `group:"routes"`
@@ -73,7 +76,7 @@ func Serve(cmd *cobra.Command, args []string) error {
engine.Use(mid.ProcessResponse)
engine.Use(mid.WeChatVerify)
- engine.Use("/t+", mid.WeChatAuthUserInfo, mid.WeChatSilentAuth)
+ engine.Use([]string{"/t/:tenant", "/t/:tenant/*"}, mid.WeChatAuth, http.Wechat.Render)
http.Service.Engine.Use(favicon.New(favicon.Config{
Data: []byte{},
diff --git a/frontend/src/components/ChargeCode.vue b/frontend/src/components/ChargeCode.vue
index 585a761..7e1ff39 100644
--- a/frontend/src/components/ChargeCode.vue
+++ b/frontend/src/components/ChargeCode.vue
@@ -3,7 +3,7 @@
刷新充值码
-
+
@@ -32,14 +32,6 @@ export default defineComponent({
return code.amount / 100 + " 元/ " + code.amount + " 点";
};
- const copyCode = (code) => {
- navigator.clipboard.writeText(code.code).then(() => {
- showSuccessToast('充值码已复制');
- }).catch((err) => {
- showFailToast('复制失败');
- });
- };
-
onMounted(() => {
loadChargeCodes();
});
@@ -47,7 +39,6 @@ export default defineComponent({
return {
codes,
getCodeAmountTitle,
- copyCode,
loadChargeCodes,
}
}
diff --git a/frontend/src/components/ChargeNoticeBar.vue b/frontend/src/components/ChargeNoticeBar.vue
index 3363186..52e780a 100644
--- a/frontend/src/components/ChargeNoticeBar.vue
+++ b/frontend/src/components/ChargeNoticeBar.vue
@@ -1,5 +1,5 @@
-
+
@@ -12,15 +12,6 @@ export default defineComponent({
contact: String,
},
setup(props) {
- const copyCode = (code) => {
- navigator.clipboard.writeText(code).then(() => {
- showSuccessToast('客服微信已复制');
- }).catch((err) => {
- showFailToast('复制失败');
- });
- };
-
-
return {
copyCode,
}
diff --git a/frontend/src/router/routes.js b/frontend/src/router/routes.js
index 030cc05..af26776 100644
--- a/frontend/src/router/routes.js
+++ b/frontend/src/router/routes.js
@@ -1,5 +1,8 @@
import NotFound from '@/views/NotFound.vue';
+import PlayView from '@/views/PlayView.vue';
+import BoughtView from '@/views/tabs/BoughtView.vue';
import HomeView from '@/views/tabs/HomeView.vue';
+import UserView from '@/views/tabs/UserView.vue';
import TabView from '@/views/TabView.vue';
const routes = [
@@ -22,7 +25,7 @@ const routes = [
{
path: 'bought',
name: 'tab.bought',
- component: () => import('@/views/tabs/BoughtView.vue'),
+ component: BoughtView,
meta: {
title: '已购买',
keepAlive: true,
@@ -35,7 +38,7 @@ const routes = [
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
- component: () => import('@/views/tabs/UserView.vue'),
+ component: UserView,
meta: {
title: '个人中心',
keepAlive: false,
@@ -46,7 +49,7 @@ const routes = [
{
path: '/t/:tenant/play/:hash',
name: 'play',
- component: () => import('@/views/PlayView.vue'),
+ component: PlayView,
meta: {
title: '播放',
keepAlive: false,
diff --git a/frontend/src/utils/copy.js b/frontend/src/utils/copy.js
new file mode 100644
index 0000000..afa6731
--- /dev/null
+++ b/frontend/src/utils/copy.js
@@ -0,0 +1,44 @@
+const copy = (text) => {
+ // 数字没有 .length 不能执行selectText 需要转化成字符串
+ const textString = text.toString();
+ const input = document.createElement('input');
+ input.id = 'copy-input';
+ input.readOnly = true; // 防止ios聚焦触发键盘事件
+ input.style.position = 'absolute';
+ input.style.left = '-1000px';
+ input.style.zIndex = '-1000';
+ document.body.appendChild(input);
+ input.value = textString;
+
+ // ios必须先选中文字且不支持 input.select();
+ selectText(input, 0, textString.length);
+
+ input.blur();
+ document.body.removeChild(input); // 使用完成后,移除 input 元素,避免占用页面高度
+
+ // input自带的select()方法在苹果端无法进行选择,所以需要自己去写一个类似的方法
+ // 选择文本。createTextRange(setSelectionRange)是input方法
+ function selectText(textBox, startIndex, stopIndex) {
+ if (textBox.createTextRange) {
+ //ie
+ const range = textBox.createTextRange();
+ range.collapse(true);
+ range.moveStart('character', startIndex); //起始光标
+ range.moveEnd('character', stopIndex - startIndex); //结束光标
+ range.select(); //不兼容苹果
+ } else {
+ //firefox/chrome
+ textBox.setSelectionRange(startIndex, stopIndex);
+ textBox.focus();
+ }
+ }
+
+ console.log(document.execCommand('copy'), 'execCommand');
+ if (document.execCommand('copy')) {
+ document.execCommand('copy');
+ return true
+ }
+ return false
+};
+
+export default copy;
\ No newline at end of file