♻️ All parts into nexus
This commit is contained in:
		
							
								
								
									
										72
									
								
								.idea/workspace.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										72
									
								
								.idea/workspace.xml
									
									
									
										generated
									
									
									
								
							| @@ -4,22 +4,8 @@ | ||||
|     <option name="autoReloadType" value="ALL" /> | ||||
|   </component> | ||||
|   <component name="ChangeListManager"> | ||||
|     <list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":recycle: Remove most of the dealer deps and move to nexus"> | ||||
|       <change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/go.sum" beforeDir="false" afterPath="$PROJECT_DIR$/go.sum" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/grpc/auth.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/grpc/auth.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/api/auth_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/auth_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/exts/auth.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/exts/auth.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/server.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/server.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/services/accounts.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/accounts.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/services/auth.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/auth.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/services/factors.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/factors.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/services/statuses.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/statuses.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/services/tokens.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/tokens.go" afterDir="false" /> | ||||
|     <list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":recycle: All parts into nexus"> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/main.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/settings.toml" beforeDir="false" afterPath="$PROJECT_DIR$/settings.toml" afterDir="false" /> | ||||
|     </list> | ||||
|     <option name="SHOW_DIALOG" value="false" /> | ||||
|     <option name="HIGHLIGHT_CONFLICTS" value="true" /> | ||||
| @@ -54,34 +40,34 @@ | ||||
|     <option name="hideEmptyMiddlePackages" value="true" /> | ||||
|     <option name="showLibraryContents" value="true" /> | ||||
|   </component> | ||||
|   <component name="PropertiesComponent"><![CDATA[{ | ||||
|   "keyToString": { | ||||
|     "DefaultGoTemplateProperty": "Go File", | ||||
|     "Go Build.Backend.executor": "Run", | ||||
|     "Go 构建.Backend.executor": "Run", | ||||
|     "RunOnceActivity.ShowReadmeOnStart": "true", | ||||
|     "RunOnceActivity.go.formatter.settings.were.checked": "true", | ||||
|     "RunOnceActivity.go.migrated.go.modules.settings": "true", | ||||
|     "RunOnceActivity.go.modules.automatic.dependencies.download": "true", | ||||
|     "RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true", | ||||
|     "git-widget-placeholder": "refactor/nexus", | ||||
|     "go.import.settings.migrated": "true", | ||||
|     "go.sdk.automatically.set": "true", | ||||
|     "last_opened_file_path": "/Users/littlesheep/Documents/Projects/Hydrogen/Passport/keys", | ||||
|     "node.js.detected.package.eslint": "true", | ||||
|     "node.js.selected.package.eslint": "(autodetect)", | ||||
|     "nodejs_package_manager_path": "npm", | ||||
|     "run.code.analysis.last.selected.profile": "pProject Default", | ||||
|     "settings.editor.selected.configurable": "go.vgo", | ||||
|     "ts.external.directory.path": "/Users/littlesheep/Documents/Projects/Hydrogen/Passport/web/node_modules/typescript/lib", | ||||
|     "vue.rearranger.settings.migration": "true" | ||||
|   <component name="PropertiesComponent">{ | ||||
|   "keyToString": { | ||||
|     "DefaultGoTemplateProperty": "Go File", | ||||
|     "Go Build.Backend.executor": "Run", | ||||
|     "Go 构建.Backend.executor": "Run", | ||||
|     "RunOnceActivity.ShowReadmeOnStart": "true", | ||||
|     "RunOnceActivity.go.formatter.settings.were.checked": "true", | ||||
|     "RunOnceActivity.go.migrated.go.modules.settings": "true", | ||||
|     "RunOnceActivity.go.modules.automatic.dependencies.download": "true", | ||||
|     "RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true", | ||||
|     "git-widget-placeholder": "refactor/nexus", | ||||
|     "go.import.settings.migrated": "true", | ||||
|     "go.sdk.automatically.set": "true", | ||||
|     "last_opened_file_path": "/Users/littlesheep/Documents/Projects/Hydrogen/Passport/keys", | ||||
|     "node.js.detected.package.eslint": "true", | ||||
|     "node.js.selected.package.eslint": "(autodetect)", | ||||
|     "nodejs_package_manager_path": "npm", | ||||
|     "run.code.analysis.last.selected.profile": "pProject Default", | ||||
|     "settings.editor.selected.configurable": "go.vgo", | ||||
|     "ts.external.directory.path": "/Users/littlesheep/Documents/Projects/Hydrogen/Passport/web/node_modules/typescript/lib", | ||||
|     "vue.rearranger.settings.migration": "true" | ||||
|   }, | ||||
|   "keyToStringList": { | ||||
|     "DatabaseDriversLRU": [ | ||||
|       "postgresql" | ||||
|   "keyToStringList": { | ||||
|     "DatabaseDriversLRU": [ | ||||
|       "postgresql" | ||||
|     ] | ||||
|   } | ||||
| }]]></component> | ||||
| }</component> | ||||
|   <component name="RecentsManager"> | ||||
|     <key name="CopyFile.RECENT_KEYS"> | ||||
|       <recent name="$PROJECT_DIR$/keys" /> | ||||
| @@ -166,8 +152,6 @@ | ||||
|     </option> | ||||
|   </component> | ||||
|   <component name="VcsManagerConfiguration"> | ||||
|     <MESSAGE value=":bug: Fix daily check issue" /> | ||||
|     <MESSAGE value=":sparkles: Can get today's daily sign record" /> | ||||
|     <MESSAGE value=":bug: Fix auto maintain range missing models" /> | ||||
|     <MESSAGE value=":bug: Fix date formatting issue on daily signing" /> | ||||
|     <MESSAGE value=":bug: Fix daily sign batch list query issue" /> | ||||
| @@ -191,7 +175,9 @@ | ||||
|     <MESSAGE value=":bug: Fix notification order... again" /> | ||||
|     <MESSAGE value=":bug: Trying to fix panic" /> | ||||
|     <MESSAGE value=":recycle: Remove most of the dealer deps and move to nexus" /> | ||||
|     <option name="LAST_COMMIT_MESSAGE" value=":recycle: Remove most of the dealer deps and move to nexus" /> | ||||
|     <MESSAGE value=":recycle: Refactored more modules into nexus" /> | ||||
|     <MESSAGE value=":recycle: All parts into nexus" /> | ||||
|     <option name="LAST_COMMIT_MESSAGE" value=":recycle: All parts into nexus" /> | ||||
|   </component> | ||||
|   <component name="VgoProject"> | ||||
|     <settings-migrated>true</settings-migrated> | ||||
|   | ||||
							
								
								
									
										13
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,15 +1,14 @@ | ||||
| module git.solsynth.dev/hydrogen/passport | ||||
|  | ||||
| go 1.22.0 | ||||
|  | ||||
| toolchain go1.23.2 | ||||
| go 1.23.2 | ||||
|  | ||||
| require ( | ||||
| 	git.solsynth.dev/hydrogen/dealer v0.0.0-20241015165700-60e4bbfd9782 | ||||
| 	git.solsynth.dev/hypernet/nexus v0.0.0-20241023163829-f51b22f0e880 | ||||
| 	git.solsynth.dev/hypernet/nexus v0.0.0-20241024155238-421834ae5c4a | ||||
| 	git.solsynth.dev/hypernet/pusher v0.0.0-20241026153052-cd2c326efa4e | ||||
| 	github.com/dgraph-io/ristretto v0.1.1 | ||||
| 	github.com/eko/gocache/lib/v4 v4.1.6 | ||||
| 	github.com/eko/gocache/store/ristretto/v4 v4.2.2 | ||||
| 	github.com/fatih/color v1.17.0 | ||||
| 	github.com/go-playground/validator/v10 v10.22.1 | ||||
| 	github.com/gofiber/fiber/v2 v2.52.5 | ||||
| 	github.com/golang-jwt/jwt/v5 v5.2.1 | ||||
| @@ -35,7 +34,6 @@ require ( | ||||
| 	github.com/beorn7/perks v1.0.1 // indirect | ||||
| 	github.com/cespare/xxhash/v2 v2.3.0 // indirect | ||||
| 	github.com/dustin/go-humanize v1.0.0 // indirect | ||||
| 	github.com/fatih/color v1.17.0 // indirect | ||||
| 	github.com/fsnotify/fsnotify v1.7.0 // indirect | ||||
| 	github.com/gabriel-vasile/mimetype v1.4.3 // indirect | ||||
| 	github.com/go-playground/form v3.1.4+incompatible // indirect | ||||
| @@ -75,6 +73,9 @@ require ( | ||||
| 	github.com/mitchellh/mapstructure v1.5.0 // indirect | ||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	github.com/nats-io/nats.go v1.37.0 // indirect | ||||
| 	github.com/nats-io/nkeys v0.4.7 // indirect | ||||
| 	github.com/nats-io/nuid v1.0.1 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.2.3 // indirect | ||||
| 	github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect | ||||
| 	github.com/pkg/errors v0.9.1 // indirect | ||||
|   | ||||
							
								
								
									
										16
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								go.sum
									
									
									
									
									
								
							| @@ -33,12 +33,10 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 | ||||
| dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | ||||
| filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= | ||||
| filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= | ||||
| git.solsynth.dev/hydrogen/dealer v0.0.0-20241015165700-60e4bbfd9782 h1:HUgt8RmDp5AKLlT/QGk4QXcO23OEMVpRYRjgLfOf45c= | ||||
| git.solsynth.dev/hydrogen/dealer v0.0.0-20241015165700-60e4bbfd9782/go.mod h1:Q51JPkKnV0UoOT/IRmdBh5CyfSlp7s8BRGzgooYHqkI= | ||||
| git.solsynth.dev/hypernet/nexus v0.0.0-20241022152358-a1412acc9084 h1:e+G3H8Hrzk3VaYwbppxIXATq8I4u/5K/g4BkqWv2L2Y= | ||||
| git.solsynth.dev/hypernet/nexus v0.0.0-20241022152358-a1412acc9084/go.mod h1:BKF6Fv/TdfMaxiRHoLjT5AxTsbVBU6VKAZbWxGAMxe4= | ||||
| git.solsynth.dev/hypernet/nexus v0.0.0-20241023163829-f51b22f0e880 h1:l6IKIMfm0XRHQSrCoTVQTrOoE3NxIhd+h/OodXGSz3g= | ||||
| git.solsynth.dev/hypernet/nexus v0.0.0-20241023163829-f51b22f0e880/go.mod h1:BKF6Fv/TdfMaxiRHoLjT5AxTsbVBU6VKAZbWxGAMxe4= | ||||
| git.solsynth.dev/hypernet/nexus v0.0.0-20241024155238-421834ae5c4a h1:yl2cnfFvvgOGwr9qoWu4QQHVpVPCI9f1idyaKt2gXxo= | ||||
| git.solsynth.dev/hypernet/nexus v0.0.0-20241024155238-421834ae5c4a/go.mod h1:68l4+CkuG3sz9aiUhbN5tq3YHrt6KUNK9FIWaGuWCdc= | ||||
| git.solsynth.dev/hypernet/pusher v0.0.0-20241026153052-cd2c326efa4e h1:DtHhMjgxS/spUt/KEdbRFtaVnepI6Vx8pbHdJaNH1hs= | ||||
| git.solsynth.dev/hypernet/pusher v0.0.0-20241026153052-cd2c326efa4e/go.mod h1:XHTqFU/vBe4JiuAjl87GUcL8+w/IizSNoqH6n3WkQFc= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | ||||
| github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= | ||||
| @@ -339,6 +337,12 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G | ||||
| github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | ||||
| github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= | ||||
| github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= | ||||
| github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE= | ||||
| github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= | ||||
| github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= | ||||
| github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= | ||||
| github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= | ||||
| github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= | ||||
| github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= | ||||
| github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= | ||||
| github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= | ||||
|   | ||||
| @@ -1,15 +0,0 @@ | ||||
| package gap | ||||
|  | ||||
| import "net" | ||||
|  | ||||
| func GetOutboundIP() (net.IP, error) { | ||||
| 	conn, err := net.Dial("udp", "1.1.1.1:80") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else { | ||||
| 		defer conn.Close() | ||||
| 	} | ||||
|  | ||||
| 	localAddr := conn.LocalAddr().(*net.UDPAddr) | ||||
| 	return localAddr.IP, nil | ||||
| } | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hypernet/nexus/pkg/nex" | ||||
| 	"git.solsynth.dev/hypernet/nexus/pkg/proto" | ||||
| 	"git.solsynth.dev/hypernet/pusher/pkg/pushkit/pushcon" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"strings" | ||||
|  | ||||
| @@ -11,18 +12,19 @@ import ( | ||||
| ) | ||||
|  | ||||
| var Nx *nex.Conn | ||||
| var Px *pushcon.Conn | ||||
|  | ||||
| func InitializeToNexus() error { | ||||
| 	grpcBind := strings.SplitN(viper.GetString("grpc_bind"), ":", 2) | ||||
| 	httpBind := strings.SplitN(viper.GetString("bind"), ":", 2) | ||||
|  | ||||
| 	outboundIp, _ := GetOutboundIP() | ||||
| 	outboundIp, _ := nex.GetOutboundIP() | ||||
|  | ||||
| 	grpcOutbound := fmt.Sprintf("%s:%s", outboundIp, grpcBind[1]) | ||||
| 	httpOutbound := fmt.Sprintf("%s:%s", outboundIp, httpBind[1]) | ||||
|  | ||||
| 	var err error | ||||
| 	Nx, err = nex.NewNexusConn(viper.GetString("dealer.addr"), &proto.ServiceInfo{ | ||||
| 	Nx, err = nex.NewNexusConn(viper.GetString("nexus_addr"), &proto.ServiceInfo{ | ||||
| 		Id:       viper.GetString("id"), | ||||
| 		Type:     nex.ServiceTypeAuth, | ||||
| 		Label:    "Passport", | ||||
| @@ -38,5 +40,10 @@ func InitializeToNexus() error { | ||||
| 		}() | ||||
| 	} | ||||
|  | ||||
| 	Px, err = pushcon.NewConn(Nx) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error during initialize pushcon: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return err | ||||
| } | ||||
|   | ||||
| @@ -24,17 +24,15 @@ func (v *Server) NotifyUser(_ context.Context, in *proto.NotifyUserRequest) (*pr | ||||
| 	metadata := nex.DecodeMap(in.GetNotify().GetMetadata()) | ||||
|  | ||||
| 	notification := models.Notification{ | ||||
| 		Topic:       in.GetNotify().GetTopic(), | ||||
| 		Title:       in.GetNotify().GetTitle(), | ||||
| 		Subtitle:    in.GetNotify().Subtitle, | ||||
| 		Body:        in.GetNotify().GetBody(), | ||||
| 		Metadata:    metadata, | ||||
| 		Avatar:      in.GetNotify().Avatar, | ||||
| 		Picture:     in.GetNotify().Picture, | ||||
| 		IsRealtime:  in.GetNotify().GetIsRealtime(), | ||||
| 		IsForcePush: in.GetNotify().GetIsForcePush(), | ||||
| 		Account:     user, | ||||
| 		AccountID:   user.ID, | ||||
| 		Topic:      in.GetNotify().GetTopic(), | ||||
| 		Title:      in.GetNotify().GetTitle(), | ||||
| 		Subtitle:   in.GetNotify().GetSubtitle(), | ||||
| 		Body:       in.GetNotify().GetBody(), | ||||
| 		Metadata:   metadata, | ||||
| 		Priority:   int(in.GetNotify().GetPriority()), | ||||
| 		IsRealtime: in.GetNotify().GetIsRealtime(), | ||||
| 		Account:    user, | ||||
| 		AccountID:  user.ID, | ||||
| 	} | ||||
|  | ||||
| 	log.Debug().Str("topic", notification.Topic).Uint("uid", notification.AccountID).Msg("Notifying user...") | ||||
| @@ -73,17 +71,15 @@ func (v *Server) NotifyUserBatch(_ context.Context, in *proto.NotifyUserBatchReq | ||||
| 		} | ||||
|  | ||||
| 		notification := models.Notification{ | ||||
| 			Topic:       in.GetNotify().GetTopic(), | ||||
| 			Title:       in.GetNotify().GetTitle(), | ||||
| 			Subtitle:    in.GetNotify().Subtitle, | ||||
| 			Body:        in.GetNotify().GetBody(), | ||||
| 			Metadata:    metadata, | ||||
| 			Avatar:      in.GetNotify().Avatar, | ||||
| 			Picture:     in.GetNotify().Picture, | ||||
| 			IsRealtime:  in.GetNotify().GetIsRealtime(), | ||||
| 			IsForcePush: in.GetNotify().GetIsForcePush(), | ||||
| 			Account:     user, | ||||
| 			AccountID:   user.ID, | ||||
| 			Topic:      in.GetNotify().GetTopic(), | ||||
| 			Title:      in.GetNotify().GetTitle(), | ||||
| 			Subtitle:   in.GetNotify().GetSubtitle(), | ||||
| 			Body:       in.GetNotify().GetBody(), | ||||
| 			Metadata:   metadata, | ||||
| 			Priority:   int(in.GetNotify().GetPriority()), | ||||
| 			IsRealtime: in.GetNotify().GetIsRealtime(), | ||||
| 			Account:    user, | ||||
| 			AccountID:  user.ID, | ||||
| 		} | ||||
| 		checklist[user.ID] = true | ||||
|  | ||||
| @@ -121,17 +117,15 @@ func (v *Server) NotifyAllUser(_ context.Context, in *proto.NotifyRequest) (*pro | ||||
| 		} | ||||
|  | ||||
| 		notification := models.Notification{ | ||||
| 			Topic:       in.GetTopic(), | ||||
| 			Title:       in.GetTitle(), | ||||
| 			Subtitle:    in.Subtitle, | ||||
| 			Body:        in.GetBody(), | ||||
| 			Metadata:    metadata, | ||||
| 			Avatar:      in.Avatar, | ||||
| 			Picture:     in.Picture, | ||||
| 			IsRealtime:  in.GetIsRealtime(), | ||||
| 			IsForcePush: in.GetIsForcePush(), | ||||
| 			Account:     user, | ||||
| 			AccountID:   user.ID, | ||||
| 			Topic:      in.GetTopic(), | ||||
| 			Title:      in.GetTitle(), | ||||
| 			Subtitle:   in.GetSubtitle(), | ||||
| 			Body:       in.GetBody(), | ||||
| 			Metadata:   metadata, | ||||
| 			Priority:   int(in.GetPriority()), | ||||
| 			IsRealtime: in.GetIsRealtime(), | ||||
| 			Account:    user, | ||||
| 			AccountID:  user.ID, | ||||
| 		} | ||||
| 		checklist[user.ID] = true | ||||
|  | ||||
|   | ||||
| @@ -10,11 +10,10 @@ type Notification struct { | ||||
|  | ||||
| 	Topic    string            `json:"topic"` | ||||
| 	Title    string            `json:"title"` | ||||
| 	Subtitle *string           `json:"subtitle"` | ||||
| 	Subtitle string            `json:"subtitle"` | ||||
| 	Body     string            `json:"body"` | ||||
| 	Metadata datatypes.JSONMap `json:"metadata"` | ||||
| 	Avatar   *string           `json:"avatar"` | ||||
| 	Picture  *string           `json:"picture"` | ||||
| 	Priority int               `json:"priority"` | ||||
| 	SenderID *uint             `json:"sender_id"` | ||||
|  | ||||
| 	Account   Account `json:"account"` | ||||
| @@ -22,8 +21,7 @@ type Notification struct { | ||||
|  | ||||
| 	ReadAt *time.Time `json:"read_at"` | ||||
|  | ||||
| 	IsRealtime  bool `json:"is_realtime" gorm:"-"` | ||||
| 	IsForcePush bool `json:"is_force_push" gorm:"-"` | ||||
| 	IsRealtime bool `json:"is_realtime" gorm:"-"` | ||||
| } | ||||
|  | ||||
| const ( | ||||
|   | ||||
| @@ -11,15 +11,13 @@ import ( | ||||
|  | ||||
| func notifyAllUser(c *fiber.Ctx) error { | ||||
| 	var data struct { | ||||
| 		Topic       string         `json:"type" validate:"required"` | ||||
| 		Title       string         `json:"subject" validate:"required,max=1024"` | ||||
| 		Subtitle    *string        `json:"subtitle" validate:"max=1024"` | ||||
| 		Body        string         `json:"content" validate:"required,max=4096"` | ||||
| 		Metadata    map[string]any `json:"metadata"` | ||||
| 		Avatar      *string        `json:"avatar"` | ||||
| 		Picture     *string        `json:"picture"` | ||||
| 		IsForcePush bool           `json:"is_force_push"` | ||||
| 		IsRealtime  bool           `json:"is_realtime"` | ||||
| 		Topic      string         `json:"type" validate:"required"` | ||||
| 		Title      string         `json:"subject" validate:"required,max=1024"` | ||||
| 		Subtitle   string         `json:"subtitle" validate:"max=1024"` | ||||
| 		Body       string         `json:"content" validate:"required,max=4096"` | ||||
| 		Metadata   map[string]any `json:"metadata"` | ||||
| 		Priority   int            `json:"priority"` | ||||
| 		IsRealtime bool           `json:"is_realtime"` | ||||
| 	} | ||||
|  | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| @@ -43,17 +41,14 @@ func notifyAllUser(c *fiber.Ctx) error { | ||||
| 	go func() { | ||||
| 		for _, user := range users { | ||||
| 			notification := models.Notification{ | ||||
| 				Topic:       data.Topic, | ||||
| 				Subtitle:    data.Subtitle, | ||||
| 				Title:       data.Title, | ||||
| 				Body:        data.Body, | ||||
| 				Metadata:    data.Metadata, | ||||
| 				Avatar:      data.Avatar, | ||||
| 				Picture:     data.Picture, | ||||
| 				IsRealtime:  data.IsRealtime, | ||||
| 				IsForcePush: data.IsForcePush, | ||||
| 				Account:     user, | ||||
| 				AccountID:   user.ID, | ||||
| 				Topic:     data.Topic, | ||||
| 				Subtitle:  data.Subtitle, | ||||
| 				Title:     data.Title, | ||||
| 				Body:      data.Body, | ||||
| 				Metadata:  data.Metadata, | ||||
| 				Priority:  data.Priority, | ||||
| 				Account:   user, | ||||
| 				AccountID: user.ID, | ||||
| 			} | ||||
|  | ||||
| 			if data.IsRealtime { | ||||
| @@ -73,14 +68,14 @@ func notifyAllUser(c *fiber.Ctx) error { | ||||
|  | ||||
| func notifyOneUser(c *fiber.Ctx) error { | ||||
| 	var data struct { | ||||
| 		Topic       string         `json:"type" validate:"required"` | ||||
| 		Title       string         `json:"subject" validate:"required,max=1024"` | ||||
| 		Subtitle    *string        `json:"subtitle" validate:"max=1024"` | ||||
| 		Body        string         `json:"content" validate:"required,max=4096"` | ||||
| 		Metadata    map[string]any `json:"metadata"` | ||||
| 		IsForcePush bool           `json:"is_force_push"` | ||||
| 		IsRealtime  bool           `json:"is_realtime"` | ||||
| 		UserID      uint           `json:"user_id" validate:"required"` | ||||
| 		Topic      string         `json:"type" validate:"required"` | ||||
| 		Title      string         `json:"subject" validate:"required,max=1024"` | ||||
| 		Subtitle   string         `json:"subtitle" validate:"max=1024"` | ||||
| 		Body       string         `json:"content" validate:"required,max=4096"` | ||||
| 		Metadata   map[string]any `json:"metadata"` | ||||
| 		Priority   int            `json:"priority"` | ||||
| 		IsRealtime bool           `json:"is_realtime"` | ||||
| 		UserID     uint           `json:"user_id" validate:"required"` | ||||
| 	} | ||||
|  | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| @@ -103,13 +98,12 @@ func notifyOneUser(c *fiber.Ctx) error { | ||||
| 	} | ||||
|  | ||||
| 	notification := models.Notification{ | ||||
| 		Topic:       data.Topic, | ||||
| 		Subtitle:    data.Subtitle, | ||||
| 		Title:       data.Title, | ||||
| 		Body:        data.Body, | ||||
| 		IsRealtime:  data.IsRealtime, | ||||
| 		IsForcePush: data.IsForcePush, | ||||
| 		AccountID:   user.ID, | ||||
| 		Topic:     data.Topic, | ||||
| 		Subtitle:  data.Subtitle, | ||||
| 		Title:     data.Title, | ||||
| 		Body:      data.Body, | ||||
| 		Priority:  data.Priority, | ||||
| 		AccountID: user.ID, | ||||
| 	} | ||||
|  | ||||
| 	if data.IsRealtime { | ||||
|   | ||||
| @@ -16,17 +16,15 @@ func notifyUser(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("user").(models.Account) | ||||
|  | ||||
| 	var data struct { | ||||
| 		ClientID    string         `json:"client_id" validate:"required"` | ||||
| 		Topic       string         `json:"type" validate:"required"` | ||||
| 		Title       string         `json:"subject" validate:"required,max=1024"` | ||||
| 		Subtitle    *string        `json:"subtitle" validate:"max=1024"` | ||||
| 		Body        string         `json:"content" validate:"required,max=4096"` | ||||
| 		Metadata    map[string]any `json:"metadata"` | ||||
| 		Avatar      *string        `json:"avatar"` | ||||
| 		Picture     *string        `json:"picture"` | ||||
| 		IsForcePush bool           `json:"is_force_push"` | ||||
| 		IsRealtime  bool           `json:"is_realtime"` | ||||
| 		UserID      uint           `json:"user_id" validate:"required"` | ||||
| 		ClientID   string         `json:"client_id" validate:"required"` | ||||
| 		Topic      string         `json:"type" validate:"required"` | ||||
| 		Title      string         `json:"subject" validate:"required,max=1024"` | ||||
| 		Subtitle   string         `json:"subtitle" validate:"max=1024"` | ||||
| 		Body       string         `json:"content" validate:"required,max=4096"` | ||||
| 		Metadata   map[string]any `json:"metadata"` | ||||
| 		Priority   int            `json:"priority"` | ||||
| 		IsRealtime bool           `json:"is_realtime"` | ||||
| 		UserID     uint           `json:"user_id" validate:"required"` | ||||
| 	} | ||||
|  | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| @@ -44,18 +42,15 @@ func notifyUser(c *fiber.Ctx) error { | ||||
| 	} | ||||
|  | ||||
| 	notification := models.Notification{ | ||||
| 		Topic:       data.Topic, | ||||
| 		Subtitle:    data.Subtitle, | ||||
| 		Title:       data.Title, | ||||
| 		Body:        data.Body, | ||||
| 		Metadata:    data.Metadata, | ||||
| 		Avatar:      data.Avatar, | ||||
| 		Picture:     data.Picture, | ||||
| 		IsRealtime:  data.IsRealtime, | ||||
| 		IsForcePush: data.IsForcePush, | ||||
| 		Account:     target, | ||||
| 		AccountID:   target.ID, | ||||
| 		SenderID:    &client.ID, | ||||
| 		Topic:     data.Topic, | ||||
| 		Subtitle:  data.Subtitle, | ||||
| 		Title:     data.Title, | ||||
| 		Body:      data.Body, | ||||
| 		Metadata:  data.Metadata, | ||||
| 		Priority:  data.Priority, | ||||
| 		Account:   target, | ||||
| 		AccountID: target.ID, | ||||
| 		SenderID:  &client.ID, | ||||
| 	} | ||||
|  | ||||
| 	if data.IsRealtime { | ||||
|   | ||||
| @@ -25,8 +25,8 @@ func NewServer() *HTTPApp { | ||||
| 	app := fiber.New(fiber.Config{ | ||||
| 		DisableStartupMessage: true, | ||||
| 		EnableIPValidation:    true, | ||||
| 		ServerHeader:          "Hydrogen.Passport", | ||||
| 		AppName:               "Hydrogen.Passport", | ||||
| 		ServerHeader:          "Hypernet.Passport", | ||||
| 		AppName:               "Hypernet.Passport", | ||||
| 		ProxyHeader:           fiber.HeaderXForwardedFor, | ||||
| 		JSONEncoder:           jsoniter.ConfigCompatibleWithStandardLibrary.Marshal, | ||||
| 		JSONDecoder:           jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal, | ||||
|   | ||||
| @@ -3,10 +3,11 @@ package services | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hypernet/nexus/pkg/nex" | ||||
| 	"git.solsynth.dev/hypernet/nexus/pkg/proto" | ||||
| 	"time" | ||||
| 	"unicode" | ||||
|  | ||||
| 	"git.solsynth.dev/hydrogen/dealer/pkg/proto" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/gap" | ||||
|  | ||||
| 	"gorm.io/gorm/clause" | ||||
| @@ -312,9 +313,12 @@ func DeleteAccount(id uint) error { | ||||
| 		return err | ||||
| 	} else { | ||||
| 		InvalidAuthCacheWithUser(id) | ||||
| 		_, _ = proto.NewServiceDirectoryClient(gap.Nx.GetNexusGrpcConn()).BroadcastDeletion(context.Background(), &proto.DeletionRequest{ | ||||
| 			ResourceType: "account", | ||||
| 			ResourceId:   fmt.Sprintf("%d", id), | ||||
| 		_, _ = proto.NewDirectoryServiceClient(gap.Nx.GetNexusGrpcConn()).BroadcastEvent(context.Background(), &proto.EventInfo{ | ||||
| 			Event: "deletion", | ||||
| 			Data: nex.EncodeMap(map[string]any{ | ||||
| 				"type": "account", | ||||
| 				"id":   fmt.Sprintf("%d", id), | ||||
| 			}), | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -1,19 +1,16 @@ | ||||
| package services | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hydrogen/dealer/pkg/proto" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/gap" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hypernet/pusher/pkg/pushkit" | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/samber/lo" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| const EmailPasswordTemplate = `Dear %s, | ||||
| @@ -86,13 +83,11 @@ func GetFactorCode(factor models.AuthFactor) (bool, error) { | ||||
| 		subject := fmt.Sprintf("[%s] Login verification code", viper.GetString("name")) | ||||
| 		content := fmt.Sprintf(EmailPasswordTemplate, user.Name, factor.Secret, viper.GetString("maintainer")) | ||||
|  | ||||
| 		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||||
| 		defer cancel() | ||||
| 		_, err := proto.NewPostmanClient(gap.Nx.GetNexusGrpcConn()).DeliverEmail(ctx, &proto.DeliverEmailRequest{ | ||||
| 		err := gap.Px.PushEmail(pushkit.EmailDeliverRequest{ | ||||
| 			To: user.GetPrimaryEmail().Content, | ||||
| 			Email: &proto.EmailRequest{ | ||||
| 				Subject:  subject, | ||||
| 				TextBody: &content, | ||||
| 			Email: pushkit.EmailData{ | ||||
| 				Subject: subject, | ||||
| 				Text:    &content, | ||||
| 			}, | ||||
| 		}) | ||||
| 		if err != nil { | ||||
|   | ||||
| @@ -4,22 +4,20 @@ import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hypernet/nexus/pkg/nex" | ||||
| 	"git.solsynth.dev/hypernet/nexus/pkg/proto" | ||||
| 	"git.solsynth.dev/hypernet/pusher/pkg/pushkit" | ||||
| 	"reflect" | ||||
| 	"time" | ||||
|  | ||||
| 	jsoniter "github.com/json-iterator/go" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/samber/lo" | ||||
|  | ||||
| 	"git.solsynth.dev/hydrogen/dealer/pkg/proto" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/gap" | ||||
|  | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| ) | ||||
|  | ||||
| // TODO Awaiting for the new notification pusher | ||||
|  | ||||
| func AddNotifySubscriber(user models.Account, provider, id, tk, ua string) (models.NotificationSubscriber, error) { | ||||
| 	var prev models.NotificationSubscriber | ||||
| 	var subscriber models.NotificationSubscriber | ||||
| @@ -101,7 +99,7 @@ func PushNotification(notification models.Notification, skipNotifiableCheck ...b | ||||
|  | ||||
| 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||||
| 	defer cancel() | ||||
| 	_, err := proto.NewStreamControllerClient(gap.Nx.GetNexusGrpcConn()).PushStream(ctx, &proto.PushStreamRequest{ | ||||
| 	_, err := proto.NewStreamServiceClient(gap.Nx.GetNexusGrpcConn()).PushStream(ctx, &proto.PushStreamRequest{ | ||||
| 		UserId: lo.ToPtr(uint64(notification.AccountID)), | ||||
| 		Body: nex.WebSocketPackage{ | ||||
| 			Action:  "notifications.new", | ||||
| @@ -131,23 +129,17 @@ func PushNotification(notification models.Notification, skipNotifiableCheck ...b | ||||
| 		tokens = append(tokens, subscriber.DeviceToken) | ||||
| 	} | ||||
|  | ||||
| 	metadata, _ := jsoniter.Marshal(notification.Metadata) | ||||
|  | ||||
| 	ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second) | ||||
| 	defer cancel() | ||||
| 	_, err = proto.NewPostmanClient(gap.Nx.GetNexusGrpcConn()).DeliverNotificationBatch(ctx, &proto.DeliverNotificationBatchRequest{ | ||||
| 		Providers:    providers, | ||||
| 		DeviceTokens: tokens, | ||||
| 		Notify: &proto.NotifyRequest{ | ||||
| 			Topic:       notification.Topic, | ||||
| 			Title:       notification.Title, | ||||
| 			Subtitle:    notification.Subtitle, | ||||
| 			Body:        notification.Body, | ||||
| 			Metadata:    metadata, | ||||
| 			Avatar:      notification.Avatar, | ||||
| 			Picture:     notification.Picture, | ||||
| 			IsRealtime:  notification.IsRealtime, | ||||
| 			IsForcePush: notification.IsForcePush, | ||||
| 	err = gap.Px.PushNotifyBatch(pushkit.NotificationPushBatchRequest{ | ||||
| 		Providers: providers, | ||||
| 		Tokens:    tokens, | ||||
| 		Notification: pushkit.Notification{ | ||||
| 			Topic:    notification.Topic, | ||||
| 			Title:    notification.Title, | ||||
| 			Subtitle: notification.Subtitle, | ||||
| 			Body:     notification.Body, | ||||
| 			Metadata: notification.Metadata, | ||||
| 		}, | ||||
| 	}) | ||||
|  | ||||
| @@ -188,7 +180,7 @@ func PushNotificationBatch(notifications []models.Notification, skipNotifiableCh | ||||
| 	var subscribers []models.NotificationSubscriber | ||||
| 	database.C.Where("account_id IN ?", accountIdx).Find(&subscribers) | ||||
|  | ||||
| 	stream := proto.NewStreamControllerClient(gap.Nx.GetNexusGrpcConn()) | ||||
| 	stream := proto.NewStreamServiceClient(gap.Nx.GetNexusGrpcConn()) | ||||
| 	for _, notification := range notifications { | ||||
| 		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||||
| 		_, _ = stream.PushStream(ctx, &proto.PushStreamRequest{ | ||||
| @@ -214,22 +206,16 @@ func PushNotificationBatch(notifications []models.Notification, skipNotifiableCh | ||||
| 			tokens = append(tokens, subscriber.DeviceToken) | ||||
| 		} | ||||
|  | ||||
| 		metadata, _ := jsoniter.Marshal(notification.Metadata) | ||||
|  | ||||
| 		ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second) | ||||
| 		_, _ = proto.NewPostmanClient(gap.Nx.GetNexusGrpcConn()).DeliverNotificationBatch(ctx, &proto.DeliverNotificationBatchRequest{ | ||||
| 			Providers:    providers, | ||||
| 			DeviceTokens: tokens, | ||||
| 			Notify: &proto.NotifyRequest{ | ||||
| 				Topic:       notification.Topic, | ||||
| 				Title:       notification.Title, | ||||
| 				Subtitle:    notification.Subtitle, | ||||
| 				Body:        notification.Body, | ||||
| 				Metadata:    metadata, | ||||
| 				Avatar:      notification.Avatar, | ||||
| 				Picture:     notification.Picture, | ||||
| 				IsRealtime:  notification.IsRealtime, | ||||
| 				IsForcePush: notification.IsForcePush, | ||||
| 		_ = gap.Px.PushNotifyBatch(pushkit.NotificationPushBatchRequest{ | ||||
| 			Providers: providers, | ||||
| 			Tokens:    tokens, | ||||
| 			Notification: pushkit.Notification{ | ||||
| 				Topic:    notification.Topic, | ||||
| 				Title:    notification.Title, | ||||
| 				Subtitle: notification.Subtitle, | ||||
| 				Body:     notification.Body, | ||||
| 				Metadata: notification.Metadata, | ||||
| 			}, | ||||
| 		}) | ||||
| 		cancel() | ||||
|   | ||||
| @@ -6,7 +6,6 @@ import ( | ||||
|  | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"github.com/samber/lo" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
|  | ||||
| @@ -131,7 +130,7 @@ func NewFriend(userA models.Account, userB models.Account, skipPending ...bool) | ||||
| 	} else { | ||||
| 		_ = NewNotification(models.Notification{ | ||||
| 			Title:     "New Friend Request", | ||||
| 			Subtitle:  lo.ToPtr(fmt.Sprintf("New friend request from %s", userA.Name)), | ||||
| 			Subtitle:  fmt.Sprintf("New friend request from %s", userA.Name), | ||||
| 			Body:      fmt.Sprintf("You got a new friend request from %s. Go to your account page and decide how to deal it.", userA.Nick), | ||||
| 			Account:   userB, | ||||
| 			AccountID: userB.ID, | ||||
| @@ -168,7 +167,7 @@ func HandleFriend(userA models.Account, userB models.Account, isAccept bool) err | ||||
|  | ||||
| 		_ = NewNotification(models.Notification{ | ||||
| 			Title:     "Friend Request Processed", | ||||
| 			Subtitle:  lo.ToPtr(fmt.Sprintf("Your friend request to %s has been processsed.", userA.Name)), | ||||
| 			Subtitle:  fmt.Sprintf("Your friend request to %s has been processsed.", userA.Name), | ||||
| 			Body:      fmt.Sprintf("Your relationship status with %s has been updated, go check it out!", userA.Nick), | ||||
| 			Account:   userB, | ||||
| 			AccountID: userB.ID, | ||||
|   | ||||
| @@ -5,7 +5,6 @@ import ( | ||||
|  | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"github.com/samber/lo" | ||||
| ) | ||||
|  | ||||
| func ListAbuseReport(account models.Account) ([]models.AbuseReport, error) { | ||||
| @@ -42,10 +41,10 @@ func UpdateAbuseReportStatus(id uint, status, message string) error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	NewNotification(models.Notification{ | ||||
| 	_ = NewNotification(models.Notification{ | ||||
| 		Topic:     "reports.feedback", | ||||
| 		Title:     "Abuse report status has been changed.", | ||||
| 		Subtitle:  lo.ToPtr(fmt.Sprintf("The report #%d's status updated", id)), | ||||
| 		Subtitle:  fmt.Sprintf("The report #%d's status updated", id), | ||||
| 		Body:      fmt.Sprintf("The report created by you with ID #%d's status has been changed to %s. Moderator message: %s", id, status, message), | ||||
| 		Account:   account, | ||||
| 		AccountID: account.ID, | ||||
|   | ||||
| @@ -3,9 +3,9 @@ package services | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hypernet/nexus/pkg/proto" | ||||
| 	"time" | ||||
|  | ||||
| 	"git.solsynth.dev/hydrogen/dealer/pkg/proto" | ||||
| 	localCache "git.solsynth.dev/hydrogen/passport/pkg/internal/cache" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/gap" | ||||
|  | ||||
| @@ -60,7 +60,7 @@ func CacheUserStatus(uid uint, status models.Status) { | ||||
| } | ||||
|  | ||||
| func GetUserOnline(uid uint) bool { | ||||
| 	pc := proto.NewStreamControllerClient(gap.Nx.GetNexusGrpcConn()) | ||||
| 	pc := proto.NewStreamServiceClient(gap.Nx.GetNexusGrpcConn()) | ||||
| 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||||
| 	defer cancel() | ||||
| 	resp, err := pc.CountStreamConnection(ctx, &proto.CountConnectionRequest{ | ||||
|   | ||||
| @@ -129,15 +129,15 @@ func ActiveTicket(ticket models.AuthTicket) (models.AuthTicket, error) { | ||||
| 		_ = NewNotification(models.Notification{ | ||||
| 			Topic:    "passport.security.alert", | ||||
| 			Title:    "New sign in alert", | ||||
| 			Subtitle: lo.ToPtr(fmt.Sprintf("New sign in from %s", ticket.IpAddress)), | ||||
| 			Subtitle: fmt.Sprintf("New sign in from %s", ticket.IpAddress), | ||||
| 			Body:     fmt.Sprintf("Your account just got a new sign in from %s. Make sure you recongize this device, or sign out it immediately and reset password.", ticket.IpAddress), | ||||
| 			Metadata: datatypes.JSONMap{ | ||||
| 				"ip_address":   ticket.IpAddress, | ||||
| 				"created_at":   ticket.CreatedAt, | ||||
| 				"available_at": ticket.AvailableAt, | ||||
| 			}, | ||||
| 			AccountID:   ticket.AccountID, | ||||
| 			IsForcePush: true, | ||||
| 			AccountID: ticket.AccountID, | ||||
| 			Priority:  5, | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,9 @@ | ||||
| package services | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hydrogen/dealer/pkg/proto" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/gap" | ||||
| 	"git.solsynth.dev/hypernet/pusher/pkg/pushkit" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| @@ -143,13 +142,11 @@ func NotifyMagicToken(token models.MagicToken) error { | ||||
| 		return fmt.Errorf("unsupported magic token type to notify") | ||||
| 	} | ||||
|  | ||||
| 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||||
| 	defer cancel() | ||||
| 	_, err := proto.NewPostmanClient(gap.Nx.GetNexusGrpcConn()).DeliverEmail(ctx, &proto.DeliverEmailRequest{ | ||||
| 	err := gap.Px.PushEmail(pushkit.EmailDeliverRequest{ | ||||
| 		To: user.GetPrimaryEmail().Content, | ||||
| 		Email: &proto.EmailRequest{ | ||||
| 			Subject:  subject, | ||||
| 			TextBody: &content, | ||||
| 		Email: pushkit.EmailData{ | ||||
| 			Subject: subject, | ||||
| 			Text:    &content, | ||||
| 		}, | ||||
| 	}) | ||||
| 	return err | ||||
|   | ||||
| @@ -1,7 +1,9 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hypernet/nexus/pkg/nex/sec" | ||||
| 	"github.com/fatih/color" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"syscall" | ||||
| @@ -27,6 +29,12 @@ func init() { | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	// Booting screen | ||||
| 	fmt.Println(color.YellowString(" ____                                _\n|  _ \\ __ _ ___ ___ _ __   ___  _ __| |_\n| |_) / _` / __/ __| '_ \\ / _ \\| '__| __|\n|  __/ (_| \\__ \\__ \\ |_) | (_) | |  | |_\n|_|   \\__,_|___/___/ .__/ \\___/|_|   \\__|\n                   |_|")) | ||||
| 	fmt.Printf("%s v%s\n", color.New(color.FgHiYellow).Add(color.Bold).Sprintf("Hypernet.Passport"), pkg.AppVersion) | ||||
| 	fmt.Printf("The user identity service in Hypernet\n") | ||||
| 	color.HiBlack("=====================================================\n") | ||||
|  | ||||
| 	// Configure settings | ||||
| 	viper.AddConfigPath(".") | ||||
| 	viper.AddConfigPath("..") | ||||
|   | ||||
| @@ -131,15 +131,13 @@ type NotifyRequest struct { | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| 	unknownFields protoimpl.UnknownFields | ||||
|  | ||||
| 	Topic       string  `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"` | ||||
| 	Title       string  `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` | ||||
| 	Subtitle    *string `protobuf:"bytes,3,opt,name=subtitle,proto3,oneof" json:"subtitle,omitempty"` | ||||
| 	Body        string  `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` | ||||
| 	Metadata    []byte  `protobuf:"bytes,5,opt,name=metadata,proto3" json:"metadata,omitempty"` | ||||
| 	Avatar      *string `protobuf:"bytes,6,opt,name=avatar,proto3,oneof" json:"avatar,omitempty"` | ||||
| 	Picture     *string `protobuf:"bytes,7,opt,name=picture,proto3,oneof" json:"picture,omitempty"` | ||||
| 	IsRealtime  bool    `protobuf:"varint,8,opt,name=is_realtime,json=isRealtime,proto3" json:"is_realtime,omitempty"` | ||||
| 	IsForcePush bool    `protobuf:"varint,9,opt,name=is_force_push,json=isForcePush,proto3" json:"is_force_push,omitempty"` | ||||
| 	Topic      string  `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"` | ||||
| 	Title      string  `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` | ||||
| 	Subtitle   *string `protobuf:"bytes,3,opt,name=subtitle,proto3,oneof" json:"subtitle,omitempty"` | ||||
| 	Body       string  `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"` | ||||
| 	Metadata   []byte  `protobuf:"bytes,5,opt,name=metadata,proto3" json:"metadata,omitempty"` | ||||
| 	Priority   int64   `protobuf:"varint,6,opt,name=priority,proto3" json:"priority,omitempty"` | ||||
| 	IsRealtime bool    `protobuf:"varint,8,opt,name=is_realtime,json=isRealtime,proto3" json:"is_realtime,omitempty"` | ||||
| } | ||||
|  | ||||
| func (x *NotifyRequest) Reset() { | ||||
| @@ -207,18 +205,11 @@ func (x *NotifyRequest) GetMetadata() []byte { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (x *NotifyRequest) GetAvatar() string { | ||||
| 	if x != nil && x.Avatar != nil { | ||||
| 		return *x.Avatar | ||||
| func (x *NotifyRequest) GetPriority() int64 { | ||||
| 	if x != nil { | ||||
| 		return x.Priority | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (x *NotifyRequest) GetPicture() string { | ||||
| 	if x != nil && x.Picture != nil { | ||||
| 		return *x.Picture | ||||
| 	} | ||||
| 	return "" | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (x *NotifyRequest) GetIsRealtime() bool { | ||||
| @@ -228,13 +219,6 @@ func (x *NotifyRequest) GetIsRealtime() bool { | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (x *NotifyRequest) GetIsForcePush() bool { | ||||
| 	if x != nil { | ||||
| 		return x.IsForcePush | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| type NotifyResponse struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| @@ -304,7 +288,7 @@ var file_notify_proto_rawDesc = []byte{ | ||||
| 	0x65, 0x72, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x02, | ||||
| 	0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, | ||||
| 	0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x06, 0x6e, 0x6f, 0x74, 0x69, | ||||
| 	0x66, 0x79, 0x22, 0xb1, 0x02, 0x0a, 0x0d, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, | ||||
| 	0x66, 0x79, 0x22, 0xd6, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, | ||||
| 	0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x01, 0x20, | ||||
| 	0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, | ||||
| 	0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, | ||||
| @@ -313,37 +297,31 @@ var file_notify_proto_rawDesc = []byte{ | ||||
| 	0x01, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, | ||||
| 	0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, | ||||
| 	0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, | ||||
| 	0x61, 0x12, 0x1b, 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, | ||||
| 	0x09, 0x48, 0x01, 0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x88, 0x01, 0x01, 0x12, 0x1d, | ||||
| 	0x0a, 0x07, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, | ||||
| 	0x02, 0x52, 0x07, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, | ||||
| 	0x61, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, | ||||
| 	0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1f, 0x0a, | ||||
| 	0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, | ||||
| 	0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x22, | ||||
| 	0x0a, 0x0d, 0x69, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x18, | ||||
| 	0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x50, 0x75, | ||||
| 	0x73, 0x68, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x42, | ||||
| 	0x09, 0x0a, 0x07, 0x5f, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x70, | ||||
| 	0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x22, 0x56, 0x0a, 0x0e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, | ||||
| 	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x73, | ||||
| 	0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, | ||||
| 	0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x66, 0x66, 0x65, 0x63, | ||||
| 	0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, | ||||
| 	0x0d, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xd6, | ||||
| 	0x01, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x0a, 0x4e, | ||||
| 	0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, | ||||
| 	0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, | ||||
| 	0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x0b, | ||||
| 	0x0a, 0x09, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x56, 0x0a, 0x0e, 0x4e, | ||||
| 	0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, | ||||
| 	0x0a, 0x69, 0x73, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, | ||||
| 	0x08, 0x52, 0x09, 0x69, 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x25, 0x0a, 0x0e, | ||||
| 	0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, | ||||
| 	0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x43, 0x6f, | ||||
| 	0x75, 0x6e, 0x74, 0x32, 0xd6, 0x01, 0x0a, 0x08, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, | ||||
| 	0x12, 0x3f, 0x0a, 0x0a, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x12, 0x18, | ||||
| 	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, | ||||
| 	0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, | ||||
| 	0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, | ||||
| 	0x00, 0x12, 0x49, 0x0a, 0x0f, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x42, | ||||
| 	0x61, 0x74, 0x63, 0x68, 0x12, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, | ||||
| 	0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, | ||||
| 	0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, | ||||
| 	0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x0f, | ||||
| 	0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, | ||||
| 	0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, | ||||
| 	0x65, 0x72, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, | ||||
| 	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, | ||||
| 	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x0d, 0x4e, 0x6f, 0x74, 0x69, 0x66, | ||||
| 	0x79, 0x41, 0x6c, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, | ||||
| 	0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, | ||||
| 	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, | ||||
| 	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x70, 0x72, 0x6f, | ||||
| 	0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, | ||||
| 	0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x0d, | ||||
| 	0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x41, 0x6c, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x2e, | ||||
| 	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, | ||||
| 	0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x74, 0x69, | ||||
| 	0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, | ||||
| 	0x2e, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, | ||||
| } | ||||
|  | ||||
| var ( | ||||
|   | ||||
| @@ -26,10 +26,8 @@ message NotifyRequest { | ||||
|   optional string subtitle = 3; | ||||
|   string body = 4; | ||||
|   bytes metadata = 5; | ||||
|   optional string avatar = 6; | ||||
|   optional string picture = 7; | ||||
|   int64 priority = 6; | ||||
|   bool is_realtime = 8; | ||||
|   bool is_force_push = 9; | ||||
| } | ||||
|  | ||||
| message NotifyResponse { | ||||
|   | ||||
| @@ -13,13 +13,12 @@ default_user_group = 1 | ||||
|  | ||||
| use_registration_magic_token = false | ||||
|  | ||||
| nexus_addr = "localhost:7001" | ||||
|  | ||||
| [debug] | ||||
| database = false | ||||
| print_routes = false | ||||
|  | ||||
| [dealer] | ||||
| addr = "127.0.0.1:7442" | ||||
|  | ||||
| [security] | ||||
| issuer = "https://solsynth.dev" | ||||
| cookie_domain = "localhost" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user