-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Support unit test in go.1 #175
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
a67793581
requested review from
IRONICBo,
PolarishT,
eurecalulu,
liumingsongning,
Cathay-Chen,
charles-chenzz,
fodedoumbouya,
tobehardest,
cubxxw,
a team,
kubbot,
StellarisW,
linesoft2 and
MC-kanon
as code owners
September 7, 2023 10:39
pull-request-size
bot
added
the
size/L
Denotes a PR that changes 100-499 lines, ignoring generated files.
label
Sep 7, 2023
CLA Assistant Lite bot 🤖 All Contributors have signed the openkf CLA. |
I have read the CLA Document and I hereby sign the CLA |
recheck |
openimbot
added a commit
to openim-sigs/cla
that referenced
this pull request
Sep 7, 2023
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>cache: Go Coverage Report</title>
<style>
body {
background: black;
color: rgb(80, 80, 80);
}
body, pre, #legend span {
font-family: Menlo, monospace;
font-weight: bold;
}
#topbar {
background: black;
position: fixed;
top: 0; left: 0; right: 0;
height: 42px;
border-bottom: 1px solid rgb(80, 80, 80);
}
#content {
margin-top: 50px;
}
#nav, #legend {
float: left;
margin-left: 10px;
}
#legend {
margin-top: 12px;
}
#nav {
margin-top: 10px;
}
#legend span {
margin: 0 5px;
}
.cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }
</style>
</head>
<body>
<div id="topbar">
<div id="nav">
<select id="files">
<option value="file0">github.com/OpenIMSDK/OpenKF/server/internal/dal/cache/redis.go (0.0%)</option>
<option value="file1">github.com/OpenIMSDK/OpenKF/server/internal/middleware/hooks/url_trie/hook.go (5.9%)</option>
<option value="file2">github.com/OpenIMSDK/OpenKF/server/internal/middleware/hooks/url_trie/trie.go (100.0%)</option>
<option value="file3">github.com/OpenIMSDK/OpenKF/server/internal/service/bot.go (0.0%)</option>
<option value="file4">github.com/OpenIMSDK/OpenKF/server/internal/service/common.go (0.0%)</option>
<option value="file5">github.com/OpenIMSDK/OpenKF/server/internal/service/community.go (0.0%)</option>
<option value="file6">github.com/OpenIMSDK/OpenKF/server/internal/service/mail.go (0.0%)</option>
<option value="file7">github.com/OpenIMSDK/OpenKF/server/internal/service/service.go (0.0%)</option>
<option value="file8">github.com/OpenIMSDK/OpenKF/server/internal/service/slack.go (0.0%)</option>
<option value="file9">github.com/OpenIMSDK/OpenKF/server/internal/service/user.go (0.0%)</option>
<option value="file10">github.com/OpenIMSDK/OpenKF/server/internal/service/user_dispatch.go (20.8%)</option>
<option value="file11">github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/auth/auth.go (92.9%)</option>
<option value="file12">github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/msg/msg.go (76.9%)</option>
<option value="file13">github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/user/user.go (90.0%)</option>
<option value="file14">github.com/OpenIMSDK/OpenKF/server/pkg/utils/password.go (100.0%)</option>
<option value="file15">github.com/OpenIMSDK/OpenKF/server/pkg/utils/strings.go (100.0%)</option>
<option value="file16">github.com/OpenIMSDK/OpenKF/server/pkg/utils/uuid.go (100.0%)</option>
<option value="file17">github.com/OpenIMSDK/OpenKF/server/pkg/utils/valid.go (100.0%)</option>
</select>
</div>
<div id="legend">
<span>not tracked</span>
<span class="cov0">not covered</span>
<span class="cov8">covered</span>
</div>
</div>
<div id="content">
<pre class="file" id="file0" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cache
import (
"context"
"time"
"github.com/go-redis/redis/v8"
"github.com/pkg/errors"
)
var _ Cache = new(cache)
// Cache cache interface.
type Cache interface {
// default
Set(ctx context.Context, key, value string, ttl time.Duration) error
Get(ctx context.Context, key string) (string, error)
TTL(ctx context.Context, key string) (time.Duration, error)
Expire(ctx context.Context, key string, ttl time.Duration) bool
ExpireAt(ctx context.Context, key string, ttl time.Time) bool
Del(ctx context.Context, key string) bool
Exists(ctx context.Context, keys ...string) bool
Incr(ctx context.Context, key string) int64
Close() error
// List
LPush(ctx context.Context, key string, values ...interface{}) error
RPush(ctx context.Context, key string, values ...interface{}) error
LPop(ctx context.Context, key string) (string, error)
RPop(ctx context.Context, key string) (string, error)
LRange(ctx context.Context, key string, start, stop int64) ([]string, error)
LRem(ctx context.Context, key string, count int64, value interface{}) (int64, error)
LTrim(ctx context.Context, key string, start, stop int64) error
LLen(ctx context.Context, key string) (int64, error)
// ZSet
ZAdd(ctx context.Context, key string, members ...*redis.Z) (int64, error)
ZRem(ctx context.Context, key string, members ...interface{}) (int64, error)
ZRange(ctx context.Context, key string, start, stop int64) ([]string, error)
// Map
HSet(ctx context.Context, key, field string, value string) error
HGet(ctx context.Context, key, field string) (string, error)
HGetAll(ctx context.Context, key string) (map[string]string, error)
HDel(ctx context.Context, key string, fields ...string) (int64, error)
HExists(ctx context.Context, key, field string) (bool, error)
HIncrBy(ctx context.Context, key, field string, incr int64) (int64, error)
HKeys(ctx context.Context, key string) ([]string, error)
HLen(ctx context.Context, key string) (int64, error)
HMGet(ctx context.Context, key string, fields ...string) ([]interface{}, error)
HMSet(ctx context.Context, key string, values ...interface{}) error
// tx
Pipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error)
Pipline() redis.Pipeliner
TxPipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error)
TxPipeline() redis.Pipeliner
Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) error
}
// cache cache dao representative.
type cache struct {
client *redis.Client
}
// Use return cache db.
func Use(client *redis.Client) Cache <span class="cov0" title="0">{
return &cache{
client: client,
}
}</span>
// Get get value.
func (c *cache) Get(ctx context.Context, key string) (string, error) <span class="cov0" title="0">{
value, err := c.client.Get(ctx, key).Result()
if err != nil </span><span class="cov0" title="0">{
return "", errors.Wrapf(err, "redis get key: %s err", key)
}</span>
<span class="cov0" title="0">return value, nil</span>
}
// Set set key value pair to redis.
func (c *cache) Set(ctx context.Context, key string, value string, ttl time.Duration) error <span class="cov0" title="0">{
if err := c.client.Set(ctx, key, value, ttl).Err(); err != nil </span><span class="cov0" title="0">{
return errors.Wrapf(err, "redis set key: %s err", key)
}</span>
<span class="cov0" title="0">return nil</span>
}
// TTL get ttl of key.
func (c *cache) TTL(ctx context.Context, key string) (time.Duration, error) <span class="cov0" title="0">{
ttl, err := c.client.TTL(ctx, key).Result()
if err != nil </span><span class="cov0" title="0">{
return -1, errors.Wrapf(err, "redis get key: %s err", key)
}</span>
<span class="cov0" title="0">return ttl, nil</span>
}
// Expire expire key.
func (c *cache) Expire(ctx context.Context, key string, ttl time.Duration) bool <span class="cov0" title="0">{
ok, _ := c.client.Expire(ctx, key, ttl).Result()
return ok
}</span>
// ExpireAt expire key at time.
func (c *cache) ExpireAt(ctx context.Context, key string, ttl time.Time) bool <span class="cov0" title="0">{
ok, _ := c.client.ExpireAt(ctx, key, ttl).Result()
return ok
}</span>
// Exists check some keys are exist.
func (c *cache) Exists(ctx context.Context, keys ...string) bool <span class="cov0" title="0">{
if len(keys) == 0 </span><span class="cov0" title="0">{
return true
}</span>
<span class="cov0" title="0">value, _ := c.client.Exists(ctx, keys...).Result()
return value > 0</span>
}
// Del delete key.
func (c *cache) Del(ctx context.Context, key string) bool <span class="cov0" title="0">{
if key == "" </span><span class="cov0" title="0">{
return true
}</span>
<span class="cov0" title="0">value, _ := c.client.Del(ctx, key).Result()
return value > 0</span>
}
// Incr incr key.
func (c *cache) Incr(ctx context.Context, key string) int64 <span class="cov0" title="0">{
value, _ := c.client.Incr(ctx, key).Result()
return value
}</span>
// Close close redis client.
func (c *cache) Close() error <span class="cov0" title="0">{
return c.client.Close()
}</span>
// LPush left push value to list.
func (c *cache) LPush(ctx context.Context, key string, values ...interface{}) error <span class="cov0" title="0">{
return c.client.LPush(ctx, key, values...).Err()
}</span>
// RPush right push value to list.
func (c *cache) RPush(ctx context.Context, key string, values ...interface{}) error <span class="cov0" title="0">{
return c.client.RPush(ctx, key, values...).Err()
}</span>
// LPop left pop value from list.
func (c *cache) LPop(ctx context.Context, key string) (string, error) <span class="cov0" title="0">{
return c.client.LPop(ctx, key).Result()
}</span>
// RPop right pop value from list.
func (c *cache) RPop(ctx context.Context, key string) (string, error) <span class="cov0" title="0">{
return c.client.RPop(ctx, key).Result()
}</span>
// LRange get list value from start to stop.
func (c *cache) LRange(ctx context.Context, key string, start, stop int64) ([]string, error) <span class="cov0" title="0">{
return c.client.LRange(ctx, key, start, stop).Result()
}</span>
// LRem remove count value from list.
func (c *cache) LRem(ctx context.Context, key string, count int64, value interface{}) (int64, error) <span class="cov0" title="0">{
return c.client.LRem(ctx, key, count, value).Result()
}</span>
// LTrim trim list from start to stop.
func (c *cache) LTrim(ctx context.Context, key string, start, stop int64) error <span class="cov0" title="0">{
return c.client.LTrim(ctx, key, start, stop).Err()
}</span>
// LLen get list length.
func (c *cache) LLen(ctx context.Context, key string) (int64, error) <span class="cov0" title="0">{
return c.client.LLen(ctx, key).Result()
}</span>
// Pipelined pipelined.
func (c *cache) Pipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error) <span class="cov0" title="0">{
return c.client.Pipelined(ctx, fn)
}</span>
// Pipline pipline.
func (c *cache) Pipline() redis.Pipeliner <span class="cov0" title="0">{
return c.client.Pipeline()
}</span>
// TxPipelined tx pipelined.
func (c *cache) TxPipelined(ctx context.Context, fn func(redis.Pipeliner) error) ([]redis.Cmder, error) <span class="cov0" title="0">{
return c.client.TxPipelined(ctx, fn)
}</span>
// TxPipeline tx pipeline.
func (c *cache) TxPipeline() redis.Pipeliner <span class="cov0" title="0">{
return c.client.TxPipeline()
}</span>
// Watch watch.
func (c *cache) Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) error <span class="cov0" title="0">{
return c.client.Watch(ctx, fn, keys...)
}</span>
// LPopAndRPush lpop and rpush.
func (c *cache) LPopAndRPush(ctx context.Context, key string) (string, error) <span class="cov0" title="0">{
tx := c.client.TxPipeline()
defer tx.Close()
// check length
length, err := c.client.LLen(ctx, key).Result()
if err != nil || length <= 1 </span><span class="cov0" title="0">{
return "", errors.Wrapf(err, "list is not exists or length less than 1: %s err", key)
}</span>
// get head value
<span class="cov0" title="0">headVal, _ := c.client.LIndex(ctx, key, 0).Result()
_ = tx.Process(ctx, c.client.LPop(ctx, key))
_ = tx.Process(ctx, c.client.RPush(ctx, key, headVal))
_, err = tx.Exec(ctx)
if err != nil </span><span class="cov0" title="0">{
return "", err
}</span>
<span class="cov0" title="0">return headVal, nil</span>
}
// ZAdd zset add.
func (c *cache) ZAdd(ctx context.Context, key string, members ...*redis.Z) (int64, error) <span class="cov0" title="0">{
return c.client.ZAdd(ctx, key, members...).Result()
}</span>
// ZRem zset remove.
func (c *cache) ZRem(ctx context.Context, key string, members ...interface{}) (int64, error) <span class="cov0" title="0">{
return c.client.ZRem(ctx, key, members...).Result()
}</span>
// ZRange zset range.
func (c *cache) ZRange(ctx context.Context, key string, start, stop int64) ([]string, error) <span class="cov0" title="0">{
return c.client.ZRange(ctx, key, start, stop).Result()
}</span>
// // EnqueueStringLPush enqueue key and push left.
// func (c *cache) EnqueueStringLPush(ctx context.Context, key, value string) error {
// c.mu.Lock()
// defer c.mu.Unlock()
// err := c.client.LPush(ctx, key, value).Err()
// if err != nil {
// return errors.Wrapf(err, "redis enqueue key: %s , value: %s, err: %s", key, value, err.Error())
// }
// return nil
// }
// // DequeueStringRPop dequeue key and pop right.
// func (c *cache) DequeueStringRPop(ctx context.Context, key string) (string, error) {
// c.mu.Lock()
// defer c.mu.Unlock()
// value, err := c.client.RPop(ctx, key).Result()
// if err != nil {
// return "", errors.Wrapf(err, "redis dequeue key: %s , err: %s", key, err.Error())
// }
// return value, nil
// }
// func (c *cache) TxPipeline(ctx context.Context) redis.Pipeliner {
// return c.client.TxPipeline()
// }
// HSet hset.
func (c *cache) HSet(ctx context.Context, key, field string, value string) error <span class="cov0" title="0">{
return c.client.HSet(ctx, key, field, value).Err()
}</span>
// HGet hget.
func (c *cache) HGet(ctx context.Context, key, field string) (string, error) <span class="cov0" title="0">{
return c.client.HGet(ctx, key, field).Result()
}</span>
// HGetAll hgetall.
func (c *cache) HGetAll(ctx context.Context, key string) (map[string]string, error) <span class="cov0" title="0">{
return c.client.HGetAll(ctx, key).Result()
}</span>
// HDel hdel.
func (c *cache) HDel(ctx context.Context, key string, fields ...string) (int64, error) <span class="cov0" title="0">{
return c.client.HDel(ctx, key, fields...).Result()
}</span>
// HExists hexists.
func (c *cache) HExists(ctx context.Context, key, field string) (bool, error) <span class="cov0" title="0">{
return c.client.HExists(ctx, key, field).Result()
}</span>
// HIncrBy hincrby.
func (c *cache) HIncrBy(ctx context.Context, key, field string, incr int64) (int64, error) <span class="cov0" title="0">{
return c.client.HIncrBy(ctx, key, field, incr).Result()
}</span>
// HKeys hkeys.
func (c *cache) HKeys(ctx context.Context, key string) ([]string, error) <span class="cov0" title="0">{
return c.client.HKeys(ctx, key).Result()
}</span>
// HLen hlen.
func (c *cache) HLen(ctx context.Context, key string) (int64, error) <span class="cov0" title="0">{
return c.client.HLen(ctx, key).Result()
}</span>
// HMGet hmget.
func (c *cache) HMGet(ctx context.Context, key string, fields ...string) ([]interface{}, error) <span class="cov0" title="0">{
return c.client.HMGet(ctx, key, fields...).Result()
}</span>
// HMSet hmset.
func (c *cache) HMSet(ctx context.Context, key string, values ...interface{}) error <span class="cov0" title="0">{
return c.client.HMSet(ctx, key, values).Err()
}</span>
</pre>
<pre class="file" id="file1" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package urltrie
import (
"net/url"
"github.com/gin-gonic/gin"
"github.com/OpenIMSDK/OpenKF/server/pkg/log"
)
// Mapping for store url pattern and hook.
var hookTrie *Trie
func init() <span class="cov8" title="1">{
hookTrie = NewTrie()
}</span>
// RegisterHook register url & hook to trie.
func RegisterHook(hook Hook) <span class="cov0" title="0">{
hookTrie.InsertBatch(hook.Patterns(), hook)
}</span>
// RunHook enable hook for interceptor.
func RunHook() gin.HandlerFunc <span class="cov0" title="0">{
return func(c *gin.Context) </span><span class="cov0" title="0">{
raw := c.Request.URL.Path
// Get path from url
p, err := url.Parse(raw)
if err != nil </span><span class="cov0" title="0">{
log.Errorf("Hook", "parse url error: %v", err)
}</span>
<span class="cov0" title="0">path := p.Path
hooks, ok := hookTrie.Match(path)
if !ok </span><span class="cov0" title="0">{
c.Next()
return
}</span>
// Run all before hooks
<span class="cov0" title="0">for _, hook := range hooks </span><span class="cov0" title="0">{
hook.BeforeRun(c)
}</span>
// Run controllers
<span class="cov0" title="0">c.Next()
// Run all after hooks
for _, hook := range hooks </span><span class="cov0" title="0">{
hook.AfterRun(c)
}</span>
}
}
</pre>
<pre class="file" id="file2" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package urltrie
import (
"sort"
"strings"
"github.com/gin-gonic/gin"
)
// Hook for interceptor.
type Hook interface {
// Register with url pattern
// Support * wildcard, you can use it like this:
// /api/v1/*, and the url like /api/v1/123 will be matched
//
// Now we support multi urls.
Patterns() []string
// Priority will set the priority of the hook
Priority() int64
// Hooks
// BeforeRun will be called before controller, you can do something here
BeforeRun(c *gin.Context)
// AfterRun will be called after controller, you can do something here
AfterRun(c *gin.Context)
}
type sortedHook []Hook
func (sh sortedHook) Len() int <span class="cov8" title="1">{ return len(sh) }</span>
func (sh sortedHook) Less(i, j int) bool <span class="cov8" title="1">{ return sh[i].Priority() > sh[j].Priority() }</span>
func (sh sortedHook) Swap(i, j int) <span class="cov8" title="1">{ sh[i], sh[j] = sh[j], sh[i] }</span>
const _wildcard = "*"
type node struct {
children map[string]*node
hooks []Hook
data string
// for wildcard
isWildcard bool
isEnd bool
}
// Trie is a tree for url.
type Trie struct {
root *node
}
// NewTrie returns a new Trie.
func NewTrie() *Trie <span class="cov8" title="1">{
return &Trie{
root: &node{
children: make(map[string]*node),
},
}
}</span>
// InsertBatch insert urls with hooks.
func (t *Trie) InsertBatch(urls []string, hooks ...Hook) <span class="cov8" title="1">{
for _, url := range urls </span><span class="cov8" title="1">{
t.insert(url, hooks...)
}</span>
}
// Insert insert url with hooks.
func (t *Trie) insert(url string, hooks ...Hook) <span class="cov8" title="1">{
current := t.root
// split url with '/'
parts := strings.Split(url, "/")
for _, part := range parts </span><span class="cov8" title="1">{
if part == "" </span><span class="cov8" title="1">{
continue</span>
}
<span class="cov8" title="1">child, exists := current.children[part]
if !exists </span><span class="cov8" title="1">{
child = &node{
children: make(map[string]*node),
data: part,
}
// match wildcard
if part == _wildcard </span><span class="cov8" title="1">{
child.isWildcard = true
}</span>
// set child node
<span class="cov8" title="1">current.children[part] = child</span>
}
<span class="cov8" title="1">current = child</span>
}
// Set hooks to last node
<span class="cov8" title="1">current.isEnd = true
current.hooks = append(current.hooks, hooks...)
sortedHooks := sortedHook(current.hooks)
sort.Sort(sortedHooks)
current.hooks = sortedHooks</span>
}
// Match match url with hooks.
func (t *Trie) Match(url string) ([]Hook, bool) <span class="cov8" title="1">{
current := t.root
parts := strings.Split(url, "/")
var matchedValues []Hook
// use stack to save matched children nodes
stack := make([]*node, 0)
for _, c := range current.children </span><span class="cov8" title="1">{
stack = append(stack, c)
}</span>
<span class="cov8" title="1">for _, part := range parts </span><span class="cov8" title="1">{
if part == "" </span><span class="cov8" title="1">{
continue</span>
}
<span class="cov8" title="1">if len(stack) == 0 </span><span class="cov8" title="1">{
return matchedValues, len(matchedValues) > 0
}</span>
// get current level length
<span class="cov8" title="1">levelLen := len(stack)
for i := 0; i < levelLen; i++ </span><span class="cov8" title="1">{
// pop
current, stack = stack[0], stack[1:]
if current.isEnd </span><span class="cov8" title="1">{
if current.isWildcard || current.data == part </span><span class="cov8" title="1">{
// Match the last node, append the values
matchedValues = append(matchedValues, current.hooks...)
}</span>
<span class="cov8" title="1">continue</span>
}
// find wildcard node or expect node
<span class="cov8" title="1">if current.isWildcard || current.data == part </span><span class="cov8" title="1">{
for _, child := range current.children </span><span class="cov8" title="1">{
stack = append(stack, child)
}</span>
}
}
}
<span class="cov8" title="1">return matchedValues, len(matchedValues) > 0</span>
}
</pre>
<pre class="file" id="file3" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package service
import (
"github.com/gin-gonic/gin"
"github.com/OpenIMSDK/OpenKF/server/internal/common"
"github.com/OpenIMSDK/OpenKF/server/internal/config"
"github.com/OpenIMSDK/OpenKF/server/internal/dal/dao"
systemroles "github.com/OpenIMSDK/OpenKF/server/internal/models/system_roles"
requestparams "github.com/OpenIMSDK/OpenKF/server/internal/params/request"
responseparams "github.com/OpenIMSDK/OpenKF/server/internal/params/response"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request"
"github.com/OpenIMSDK/OpenKF/server/pkg/utils"
)
// BotService bot service.
type BotService struct {
Service
SysBotDao *dao.SysBotDao
SysCommunityDao *dao.SysCommunityDao
}
// NewBotService return new service with gin context.
func NewBotService(c *gin.Context) *BotService <span class="cov0" title="0">{
return &BotService{
Service: Service{
ctx: c,
},
SysBotDao: dao.NewSysBotDao(),
SysCommunityDao: dao.NewSysCommunityDao(),
}
}</span>
// CreateBot create bot.
func (svc *BotService) CreateBot(cid string, params *requestparams.CreateBotParams) (string, uint, error) <span class="cov0" title="0">{
// Create bot
uid := utils.GenUUIDWithoutHyphen()
communityInfo, err := svc.SysCommunityDao.FindFirstByUUID(cid)
if err != nil </span><span class="cov0" title="0">{
return "", 0, err
}</span>
<span class="cov0" title="0">bot := &systemroles.SysBot{
UUID: uid,
BotAddr: params.BotAddr,
BotPort: params.BotPort,
BotToken: params.BotToken,
Nickname: params.Nickname,
Avatar: *params.Avatar,
Description: *params.Description,
CommunityId: communityInfo.Id,
}
if err = svc.SysBotDao.Create(bot); err != nil </span><span class="cov0" title="0">{
return uid, 0, err
}</span>
<span class="cov0" title="0">b, _ := svc.SysBotDao.FindFirstByUUID(uid)
// Register bot to OpenIM
param := &request.RegisterUserParams{
Secret: config.Config.OpenIM.Secret,
Users: []request.User{
{
UserID: uid,
Nickname: params.Nickname,
FaceURL: "", // Use OpenKF avatar
},
},
}
ok, err := registerUserToOpenIM(param)
if err != nil || !ok </span><span class="cov0" title="0">{
// Assume that the user has been created/deleted successfully
_ = svc.SysBotDao.Delete(b)
return uid, b.Id, err
}</span>
<span class="cov0" title="0">return uid, b.Id, nil</span>
}
// GetCommunityBotList get community bot list.
func (svc *BotService) GetCommunityBotList(cid string, params *requestparams.ListPageParams) (*responseparams.ListPageResponse, error) <span class="cov0" title="0">{
resp := &responseparams.ListPageResponse{}
botInfos := make([]*responseparams.BotInfoResponse, 0)
csvc := NewCommunityService((svc.ctx).(*gin.Context))
info, err := csvc.GetCommunityInfoByUUIDV2(cid)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
// Get bot list
<span class="cov0" title="0">bots, total, err := svc.SysBotDao.FindByCommunityIdPage(info.Id, (params.Page-1)*params.PageSize, params.PageSize)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
// Fill response data
<span class="cov0" title="0">for _, b := range bots </span><span class="cov0" title="0">{
botInfos = append(botInfos, &responseparams.BotInfoResponse{
UUID: b.UUID,
BotAddr: b.BotAddr,
BotPort: b.BotPort,
BotToken: b.BotToken,
Nickname: b.Nickname,
Avatar: b.Avatar,
Description: b.Description,
})
}</span>
<span class="cov0" title="0">resp.List = botInfos
resp.Page = params.Page
resp.PageSize = params.PageSize
resp.Total = int(total)
return resp, nil</span>
}
// DeleteBot delete a bot.
func (svc *BotService) DeleteBot(uid string) error <span class="cov0" title="0">{
if uid == "" </span><span class="cov0" title="0">{
return common.NewError(common.I_INVALID_PARAM)
}</span>
<span class="cov0" title="0">u, err := svc.SysBotDao.FindFirstByUUID(uid)
if err != nil </span><span class="cov0" title="0">{
return err
}</span>
<span class="cov0" title="0">err = svc.SysBotDao.Delete(u)
if err != nil </span><span class="cov0" title="0">{
return err
}</span>
<span class="cov0" title="0">return nil</span>
}
// UpdateBotInfo update bot info.
func (svc *BotService) UpdateBotInfo(params *requestparams.UpdateBotParams) (*responseparams.BotInfoResponse, error) <span class="cov0" title="0">{
resp := &responseparams.BotInfoResponse{}
if params.UUID == "" </span><span class="cov0" title="0">{
return resp, common.NewError(common.I_INVALID_PARAM)
}</span>
<span class="cov0" title="0">b, err := svc.SysBotDao.FindFirstByUUID(params.UUID)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
// Update bot info
<span class="cov0" title="0">if params.BotAddr != nil </span><span class="cov0" title="0">{
b.BotAddr = *params.BotAddr
}</span>
<span class="cov0" title="0">if params.BotPort != nil </span><span class="cov0" title="0">{
b.BotPort = *params.BotPort
}</span>
<span class="cov0" title="0">if params.BotToken != nil </span><span class="cov0" title="0">{
b.BotToken = *params.BotToken
}</span>
<span class="cov0" title="0">if params.Nickname != nil </span><span class="cov0" title="0">{
b.Nickname = *params.Nickname
}</span>
<span class="cov0" title="0">if params.Avatar != nil </span><span class="cov0" title="0">{
b.Avatar = *params.Avatar
}</span>
<span class="cov0" title="0">if params.Description != nil </span><span class="cov0" title="0">{
b.Description = *params.Description
}</span>
// Update user info
<span class="cov0" title="0">if err = svc.SysBotDao.Update(b); err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
// Get new info
<span class="cov0" title="0">b, _ = svc.SysBotDao.FindFirstByUUID(params.UUID)
resp.UUID = b.UUID
resp.BotAddr = b.BotAddr
resp.BotPort = b.BotPort
resp.BotToken = b.BotToken
resp.Nickname = b.Nickname
resp.Avatar = b.Avatar
resp.Description = b.Description
return resp, nil</span>
}
</pre>
<pre class="file" id="file4" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package service
import (
"mime/multipart"
"github.com/gin-gonic/gin"
"github.com/OpenIMSDK/OpenKF/server/internal/conn/client"
"github.com/OpenIMSDK/OpenKF/server/internal/utils"
)
// CommonService community service.
type CommonService struct {
Service
}
// NewCommonService return new service with gin context.
func NewCommonService(c *gin.Context) *CommonService <span class="cov0" title="0">{
return &CommonService{
Service: Service{
ctx: c,
},
}
}</span>
// UploadFile upload file.
func (svc *CommonService) UploadFile(file *multipart.FileHeader) (string, error) <span class="cov0" title="0">{
fileHandler, err := file.Open()
if err != nil </span><span class="cov0" title="0">{
return "", err
}</span>
<span class="cov0" title="0">defer fileHandler.Close()
filename := file.Filename
objectName := utils.GenerateObjectName(filename)
fileSize := file.Size
err = client.PutObject(objectName, fileHandler, fileSize)
if err != nil </span><span class="cov0" title="0">{
return "", err
}</span>
<span class="cov0" title="0">url := client.GetObejctURL(objectName)
return url, nil</span>
}
</pre>
<pre class="file" id="file5" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package service
import (
"github.com/gin-gonic/gin"
"github.com/OpenIMSDK/OpenKF/server/internal/common"
"github.com/OpenIMSDK/OpenKF/server/internal/dal/dao"
systemroles "github.com/OpenIMSDK/OpenKF/server/internal/models/system_roles"
requestparams "github.com/OpenIMSDK/OpenKF/server/internal/params/request"
responseparams "github.com/OpenIMSDK/OpenKF/server/internal/params/response"
"github.com/OpenIMSDK/OpenKF/server/pkg/utils"
)
// CommunityService community service.
type CommunityService struct {
Service
SysCommunityDao *dao.SysCommunityDao
}
// NewCommunityService return new service with gin context.
func NewCommunityService(c *gin.Context) *CommunityService <span class="cov0" title="0">{
return &CommunityService{
Service: Service{
ctx: c,
},
SysCommunityDao: dao.NewSysCommunityDao(),
}
}</span>
// Create create community.
func (svc *CommunityService) Create(community *requestparams.CommunityParams) (string, uint, error) <span class="cov0" title="0">{
uuid := utils.GenUUIDWithoutHyphen()
if community.Description == nil </span><span class="cov0" title="0">{
*community.Description = ""
}</span>
<span class="cov0" title="0">if community.Avatar == nil </span><span class="cov0" title="0">{
*community.Avatar = ""
}</span>
<span class="cov0" title="0">data := &systemroles.SysCommunity{
UUID: uuid,
Name: community.Name,
Email: community.Email,
Description: *community.Description,
Avatar: *community.Avatar,
}
// err := db.GetMysqlDB().Create(data).Error
err := svc.SysCommunityDao.Create(data)
if err != nil </span><span class="cov0" title="0">{
return uuid, 0, err
}</span>
// Get community id
<span class="cov0" title="0">c, _ := svc.SysCommunityDao.FindFirstByUUID(uuid)
return c.UUID, c.Id, nil</span>
}
// GetCommunityInfoById get community info by id.
func (svc *CommunityService) GetCommunityInfoById(id uint) (*responseparams.CommunityInfoResponse, error) <span class="cov0" title="0">{
resp := &responseparams.CommunityInfoResponse{}
if id <= 0 </span><span class="cov0" title="0">{
return resp, common.NewError(common.I_INVALID_PARAM)
}</span>
<span class="cov0" title="0">c, err := svc.SysCommunityDao.FindFirstById(id)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
<span class="cov0" title="0">resp.UUID = c.UUID
resp.Email = c.Email
resp.Avatar = c.Avatar
resp.Name = c.Name
resp.Description = c.Description
return resp, err</span>
}
// GetCommunityInfoByUUID get community info by uuid.
func (svc *CommunityService) GetCommunityInfoByUUID(uid string) (*responseparams.CommunityInfoResponse, error) <span class="cov0" title="0">{
resp := &responseparams.CommunityInfoResponse{}
if uid == "" </span><span class="cov0" title="0">{
return resp, common.NewError(common.I_INVALID_PARAM)
}</span>
<span class="cov0" title="0">c, err := svc.SysCommunityDao.FindFirstByUUID(uid)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
<span class="cov0" title="0">resp.UUID = c.UUID
resp.Email = c.Email
resp.Avatar = c.Avatar
resp.Name = c.Name
resp.Description = c.Description
return resp, nil</span>
}
// GetCommunityInfoByUUIDV2 get community info by uuid and return SysCommunity.
func (svc *CommunityService) GetCommunityInfoByUUIDV2(uid string) (*systemroles.SysCommunity, error) <span class="cov0" title="0">{
if uid == "" </span><span class="cov0" title="0">{
return nil, common.NewError(common.I_INVALID_PARAM)
}</span>
<span class="cov0" title="0">c, err := svc.SysCommunityDao.FindFirstByUUID(uid)
if err != nil </span><span class="cov0" title="0">{
return nil, err
}</span>
<span class="cov0" title="0">return c, nil</span>
}
// UpdateCommunity update community info.
func (svc *CommunityService) UpdateCommunity(uid string, community *requestparams.UpdateCommunityInfoParams) (*responseparams.CommunityInfoResponse, error) <span class="cov0" title="0">{
resp := &responseparams.CommunityInfoResponse{}
if uid == "" </span><span class="cov0" title="0">{
return resp, common.NewError(common.I_INVALID_PARAM)
}</span>
<span class="cov0" title="0">info, err := svc.SysCommunityDao.FindFirstByUUID(uid)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
<span class="cov0" title="0">if community.Name != nil </span><span class="cov0" title="0">{
info.Name = *community.Name
}</span>
<span class="cov0" title="0">if community.Email != nil </span><span class="cov0" title="0">{
info.Email = *community.Email
}</span>
<span class="cov0" title="0">if community.Description != nil </span><span class="cov0" title="0">{
info.Description = *community.Description
}</span>
<span class="cov0" title="0">if community.Avatar != nil </span><span class="cov0" title="0">{
info.Avatar = *community.Avatar
}</span>
// Update community info
<span class="cov0" title="0">err = svc.SysCommunityDao.Update(info)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
<span class="cov0" title="0">resp.UUID = info.UUID
resp.Email = info.Email
resp.Avatar = info.Avatar
resp.Name = info.Name
resp.Description = info.Description
return resp, nil</span>
}
</pre>
<pre class="file" id="file6" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package service
import (
"time"
"github.com/gin-gonic/gin"
"github.com/OpenIMSDK/OpenKF/server/internal/common"
"github.com/OpenIMSDK/OpenKF/server/internal/conn/client"
"github.com/OpenIMSDK/OpenKF/server/internal/conn/db"
"github.com/OpenIMSDK/OpenKF/server/internal/dal/cache"
"github.com/OpenIMSDK/OpenKF/server/internal/utils"
pkgutils "github.com/OpenIMSDK/OpenKF/server/pkg/utils"
)
// MailService mail service.
type MailService struct {
Service
cache cache.Cache
}
// NewMailService return new service with gin context.
func NewMailService(c *gin.Context) *MailService <span class="cov0" title="0">{
return &MailService{
Service: Service{
ctx: c,
},
cache: cache.Use(db.GetRedis()),
}
}</span>
// SendCode send code to email.
func (svc *MailService) SendCode(email string) (err error) <span class="cov0" title="0">{
if !pkgutils.IsValidEmail(email) </span><span class="cov0" title="0">{
return common.NewError(common.I_INVALID_PARAM)
}</span>
// Check the code is exist.
<span class="cov0" title="0">code, err := svc.cache.Get(svc.ctx, "code:"+email)
// Refresh code.
if err != nil || code == "" </span><span class="cov0" title="0">{
code = utils.GenerateCode()
// save code in 60s
err = svc.cache.Set(svc.ctx, "code:"+email, code, time.Second*60)
return err
}</span>
// Generate code.
<span class="cov0" title="0">err = client.SendEmail(email, "OpenKF Admin Register", "Your verification code is "+code)
return err</span>
}
// CheckCode check code.
func (svc *MailService) CheckCode(email, code string) bool <span class="cov0" title="0">{
if !pkgutils.IsValidEmail(email) </span><span class="cov0" title="0">{
return false
}</span>
// Check the code is exist.
<span class="cov0" title="0">c, err := svc.cache.Get(svc.ctx, "code:"+email)
if err != nil </span><span class="cov0" title="0">{
return false
}</span>
<span class="cov0" title="0">if c == code </span><span class="cov0" title="0">{
return true
}</span>
<span class="cov0" title="0">return false</span>
}
</pre>
<pre class="file" id="file7" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package service
import (
"context"
)
// Service service.
type Service struct {
ctx context.Context
}
// GetCtx get context.
func (svc *Service) GetCtx() context.Context <span class="cov0" title="0">{
return svc.ctx
}</span>
</pre>
<pre class="file" id="file8" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package service
import (
"context"
"encoding/json"
"fmt"
"net"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
"github.com/shomali11/slacker"
"github.com/slack-go/slack"
"github.com/OpenIMSDK/OpenKF/server/internal/config"
"github.com/OpenIMSDK/OpenKF/server/internal/dal/dao"
"github.com/OpenIMSDK/OpenKF/server/internal/models/base"
customerroles "github.com/OpenIMSDK/OpenKF/server/internal/models/customer_roles"
responseparams "github.com/OpenIMSDK/OpenKF/server/internal/params/response"
"github.com/OpenIMSDK/OpenKF/server/pkg/log"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/constant"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/msg"
"github.com/OpenIMSDK/OpenKF/server/pkg/utils"
)
// SlackService slack service.
type SlackService struct {
Service
CustomerSlackDao *dao.CustomerSlackDao
}
// NewSlackService return new service with Background context.
func NewSlackService(ctx context.Context) *SlackService <span class="cov0" title="0">{
return &SlackService{
Service: Service{
ctx: ctx,
},
CustomerSlackDao: dao.NewCustomerSlackDao(),
}
}</span>
// GetSlackConfig get slack config.
func (svc *SlackService) GetSlackConfig() (*responseparams.SlackConfigResponse, error) <span class="cov0" title="0">{
return &responseparams.SlackConfigResponse{
BotToken: config.Config.Slack.BotToken,
AppToken: config.Config.Slack.AppToken,
AppID: config.Config.Slack.AppID,
ClientID: config.Config.Slack.ClientID,
ClientSecret: config.Config.Slack.ClientSecret,
SigningSecret: config.Config.Slack.SigningSecret,
VerificationToken: config.Config.Slack.VerificationToken,
}, nil
}</span>
// CreateCustomer create customer if not exists.
func (svc *SlackService) CreateCustomer(userId string, profile *slack.UserProfile) (string, uint, error) <span class="cov0" title="0">{
// check if exists
temp, err := svc.CustomerSlackDao.FindFirstByUUID(userId)
if temp != nil || err == nil </span><span class="cov0" title="0">{
return temp.UUID, temp.Id, err
}</span>
// Set unique email
<span class="cov0" title="0">if profile.Email == "" </span><span class="cov0" title="0">{
profile.Email = fmt.Sprintf("%s %s", "NOEMAIL", utils.GenUUIDWithoutHyphen())
}</span>
<span class="cov0" title="0">customerSlack := customerroles.CustomerSlack{
UserBase: base.UserBase{
// UUID: fmt.Sprintf("%s%s", dao.SLACK_PERFIX, userId),
UUID: userId,
Email: profile.Email,
Nickname: profile.FirstName,
Avatar: profile.Image512,
Description: profile.Title,
IsEnable: true,
},
FirstName: profile.FirstName,
LastName: profile.LastName,
RealName: profile.RealName,
RealNameNormalized: profile.RealNameNormalized,
DisplayName: profile.DisplayName,
DisplayNameNormalized: profile.DisplayNameNormalized,
Skype: profile.Skype,
Phone: profile.Phone,
Image24: profile.Image24,
Image32: profile.Image32,
Image48: profile.Image48,
Image72: profile.Image72,
Image192: profile.Image192,
Image512: profile.Image512,
ImageOriginal: profile.ImageOriginal,
Title: profile.Title,
BotID: profile.BotID,
ApiAppID: profile.ApiAppID,
StatusText: profile.StatusText,
StatusEmoji: profile.StatusEmoji,
StatusExpiration: profile.StatusExpiration,
Team: profile.Team,
}
if err = svc.CustomerSlackDao.Create(&customerSlack); err != nil </span><span class="cov0" title="0">{
return customerSlack.UUID, 0, err
}</span>
<span class="cov0" title="0">s, _ := svc.CustomerSlackDao.FindFirstByUUID(userId)
param := &request.RegisterUserParams{
Secret: config.Config.OpenIM.Secret,
Users: []request.User{
{
UserID: userId,
Nickname: customerSlack.Nickname,
FaceURL: "", // Use OpenKF avatar
},
},
}
ok, err := registerUserToOpenIM(param)
if err != nil || !ok </span><span class="cov0" title="0">{
// Assume that the user has been created/deleted successfully
_ = svc.CustomerSlackDao.Delete(s)
return userId, s.Id, err
}</span>
<span class="cov0" title="0">return userId, s.Id, nil</span>
}
// GetSlackUser get slack user.
func (svc *SlackService) GetSlackUser(userId string) (*customerroles.CustomerSlack, error) <span class="cov0" title="0">{
return svc.CustomerSlackDao.FindFirstByUUID(userId)
}</span>
// SendMsg send message to openkf.
func (svc *SlackService) SendMsg(uid, question string, botContext slacker.BotContext) error <span class="cov0" title="0">{
udSvc := NewUserDispatchService(svc.ctx)
// Get default staff id
sMap := udSvc.GetSlackMap(uid)
staffId := sMap.StaffID
if sMap.StaffID == "" || sMap.SlackChannelID == "" </span><span class="cov0" title="0">{
// Get staff id
tempStaffId, err := udSvc.GetUser()
if err != nil </span><span class="cov0" title="0">{
return err
}</span>
// Store staff, writer to cache redis map
<span class="cov0" title="0">err = udSvc.SetSlackMap(uid, tempStaffId, botContext)
if err != nil </span><span class="cov0" title="0">{
return errors.Wrapf(err, "set slack map failed")
}</span>
<span class="cov0" title="0">staffId = tempStaffId</span>
}
// Get OpenIM admin token
<span class="cov0" title="0">uSvc := NewUserService(&gin.Context{}) // TODO: Change context to same
token, err := uSvc.GetAdminToken()
if err != nil </span><span class="cov0" title="0">{
return errors.Wrapf(err, "get admin token failed")
}</span>
// Get custom info
<span class="cov0" title="0">customer, err := svc.CustomerSlackDao.FindFirstByUUID(uid)
if err != nil </span><span class="cov0" title="0">{
return errors.Wrapf(err, "find customer failed")
}</span>
<span class="cov0" title="0">msgInfo := &request.MsgInfo{
SendID: uid,
RecvID: staffId,
GroupID: "",
SenderNickname: customer.Nickname,
SenderFaceURL: customer.Avatar,
SenderPlatformID: constant.PLATFORMID_WEB,
Content: &request.TextContent{
Text: fmt.Sprintf("{\"content\":\"%s\"}", question),
},
ContentType: constant.CONTENT_TYPE_TEXT,
SessionType: constant.SESSION_TYPE_SINGLE_CHAT,
IsOnlineOnly: false,
NotOfflinePush: false,
OfflinePushInfo: &request.OfflinePushInfo{},
}
res, err := json.Marshal(msgInfo)
if err != nil </span><span class="cov0" title="0">{
return errors.Wrapf(err, "marshal msgInfo failed")
}</span>
<span class="cov0" title="0">log.Debugf("msgInfo", string(res))
host := fmt.Sprintf("http://%s", net.JoinHostPort(config.Config.OpenIM.Ip, fmt.Sprintf("%d", config.Config.OpenIM.ApiPort)))
resp, err := msg.AdminSendMsg(msgInfo, "sendMsg:"+uid, host, token.Token)
if err != nil </span><span class="cov0" title="0">{
return errors.Wrapf(err, "send msg failed")
}</span>
<span class="cov0" title="0">log.Debugf("AdminSendMsg", "Resp: %+v", resp)
return nil</span>
}
</pre>
<pre class="file" id="file9" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package service
import (
"errors"
"fmt"
"net"
"github.com/gin-gonic/gin"
"github.com/OpenIMSDK/OpenKF/server/internal/common"
"github.com/OpenIMSDK/OpenKF/server/internal/config"
"github.com/OpenIMSDK/OpenKF/server/internal/dal/dao"
"github.com/OpenIMSDK/OpenKF/server/internal/models/base"
systemroles "github.com/OpenIMSDK/OpenKF/server/internal/models/system_roles"
requestparams "github.com/OpenIMSDK/OpenKF/server/internal/params/request"
responseparams "github.com/OpenIMSDK/OpenKF/server/internal/params/response"
internal_utils "github.com/OpenIMSDK/OpenKF/server/internal/utils"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/response"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/auth"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/sdk/user"
"github.com/OpenIMSDK/OpenKF/server/pkg/utils"
)
// UserService user service.
type UserService struct {
Service
SysUserDao *dao.SysUserDao
SysCommunityDao *dao.SysCommunityDao
}
// NewUserService return new service with gin context.
func NewUserService(c *gin.Context) *UserService <span class="cov0" title="0">{
return &UserService{
Service: Service{
ctx: c,
},
SysUserDao: dao.NewSysUserDao(),
SysCommunityDao: dao.NewSysCommunityDao(),
}
}</span>
// CreateAdmin create admin user.
func (svc *UserService) CreateAdmin(user *requestparams.RegisterAdminParams) (string, uint, error) <span class="cov0" title="0">{
// Check code
mService := NewMailService((svc.ctx).(*gin.Context))
if isExist := mService.CheckCode(user.UserInfo.Email, user.Code); !isExist </span><span class="cov0" title="0">{
return "", 0, errors.New("code is not valid")
}</span>
// Create community
<span class="cov0" title="0">communityParam := user.CommunityInfo
cService := NewCommunityService((svc.ctx).(*gin.Context))
_, cid, err := cService.Create(&communityParam)
if err != nil </span><span class="cov0" title="0">{
return "", 0, err
}</span>
// Create admin
<span class="cov0" title="0">uuid := utils.GenUUIDWithoutHyphen()
adminParam := user.UserInfo
admin := &systemroles.SysUser{
UserBase: base.UserBase{
UUID: uuid,
Email: adminParam.Email,
Nickname: adminParam.Nickname,
Avatar: *adminParam.Avatar,
IsEnable: true,
},
IsAdmin: true,
Password: utils.EncryptPassword(adminParam.Password),
CommunityId: cid,
}
if err = svc.SysUserDao.Create(admin); err != nil </span><span class="cov0" title="0">{
return uuid, 0, err
}</span>
<span class="cov0" title="0">u, _ := svc.SysUserDao.FindFirstByUUID(uuid)
// TODO: set pipline to tx.
param := &request.RegisterUserParams{
Secret: config.Config.OpenIM.Secret,
Users: []request.User{
{
UserID: uuid,
Nickname: adminParam.Nickname,
FaceURL: "", // Use OpenKF avatar
},
},
}
ok, err := registerUserToOpenIM(param)
if err != nil || !ok </span><span class="cov0" title="0">{
// Assume that the user has been created/deleted successfully
_ = svc.SysUserDao.Delete(u)
return uuid, u.Id, err
}</span>
<span class="cov0" title="0">return uuid, u.Id, nil</span>
}
// CreateStaff create staff user.
func (svc *UserService) CreateStaff(cid string, user *requestparams.RegisterStaffParams) (string, uint, error) <span class="cov0" title="0">{
// Create staff
uid := utils.GenUUIDWithoutHyphen()
communityInfo, err := svc.SysCommunityDao.FindFirstByUUID(cid)
if err != nil </span><span class="cov0" title="0">{
return "", 0, err
}</span>
<span class="cov0" title="0">staffParam := user.UserInfo
staff := &systemroles.SysUser{
UserBase: base.UserBase{
UUID: uid,
Email: staffParam.Email,
Nickname: staffParam.Nickname,
Avatar: *staffParam.Avatar,
IsEnable: true,
},
IsAdmin: false,
Password: utils.EncryptPassword(staffParam.Password),
CommunityId: communityInfo.Id,
}
if err = svc.SysUserDao.Create(staff); err != nil </span><span class="cov0" title="0">{
return uid, 0, err
}</span>
// TODO: Send email to staff
<span class="cov0" title="0">u, _ := svc.SysUserDao.FindFirstByUUID(uid)
// TODO: set pipline to tx.
param := &request.RegisterUserParams{
Secret: config.Config.OpenIM.Secret,
Users: []request.User{
{
UserID: uid,
Nickname: staffParam.Nickname,
FaceURL: "", // Use OpenKF avatar
},
},
}
ok, err := registerUserToOpenIM(param)
if err != nil || !ok </span><span class="cov0" title="0">{
// Assume that the user has been created/deleted successfully
_ = svc.SysUserDao.Delete(u)
return uid, u.Id, err
}</span>
<span class="cov0" title="0">return uid, u.Id, nil</span>
}
// registerUserToOpenIM register user to openim.
func registerUserToOpenIM(param *request.RegisterUserParams) (bool, error) <span class="cov0" title="0">{
// TODO: Add get operationID
// Default not use tls/ssl
host := fmt.Sprintf("http://%s", net.JoinHostPort(config.Config.OpenIM.Ip, fmt.Sprintf("%d", config.Config.OpenIM.ApiPort)))
resp, err := user.RegisterUser(param, "registerUserToOpenIM", host)
if err != nil </span><span class="cov0" title="0">{
return false, err
}</span>
<span class="cov0" title="0">if resp.ErrCode != 0 </span><span class="cov0" title="0">{
return false, errors.New(resp.ErrMsg)
}</span>
<span class="cov0" title="0">return true, nil</span>
}
// DeleteStaff delete staff user.
func (svc *UserService) DeleteStaff(uid string) error <span class="cov0" title="0">{
if uid == "" </span><span class="cov0" title="0">{
return common.NewError(common.I_INVALID_PARAM)
}</span>
<span class="cov0" title="0">u, err := svc.SysUserDao.FindFirstByUUID(uid)
if err != nil </span><span class="cov0" title="0">{
return err
}</span>
<span class="cov0" title="0">err = svc.SysUserDao.Delete(u)
if err != nil </span><span class="cov0" title="0">{
return err
}</span>
<span class="cov0" title="0">return nil</span>
}
// LoginWithAccount login with account.
func (svc *UserService) LoginWithAccount(param *requestparams.LoginParamsWithAccount) (*responseparams.UserTokenResponse, error) <span class="cov0" title="0">{
resp := &responseparams.UserTokenResponse{}
// Check user
u, err := svc.SysUserDao.FindFirstByEmail(param.Email)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
// Check password
<span class="cov0" title="0">if !utils.ComparePassword(param.Password, u.Password) </span><span class="cov0" title="0">{
return resp, errors.New("password is not correct")
}</span>
// Get community id
<span class="cov0" title="0">cService := NewCommunityService((svc.ctx).(*gin.Context))
c, err := cService.GetCommunityInfoById(u.CommunityId)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
// Generate KF token
<span class="cov0" title="0">kfToken, kfExpireTimeSeconds, err := internal_utils.GenerateJwtToken(u.UUID, c.UUID)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
// Get IM token
<span class="cov0" title="0">imParam := &request.UserTokenParams{
Secret: config.Config.OpenIM.Secret,
UserID: u.UUID,
PlatformID: uint(config.Config.OpenIM.PlatformID),
}
imResp, err := getUserIMToken(imParam)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
// Fill response data
<span class="cov0" title="0">resp.UUID = u.UUID
resp.KFToken = &responseparams.TokenResponse{
Token: kfToken,
ExpireTimeSeconds: kfExpireTimeSeconds,
}
resp.IMToken = &responseparams.TokenResponse{
Token: imResp.Token,
ExpireTimeSeconds: imResp.ExpireTimeSeconds,
}
// TODO: Set Online in OpenIM or do this in js-sdk
return resp, nil</span>
}
// getUserIMToken get user im token.
func getUserIMToken(param *request.UserTokenParams) (*response.TokenData, error) <span class="cov0" title="0">{
// TODO: Add operationID
// Default not use tls/ssl
host := fmt.Sprintf("http://%s", net.JoinHostPort(config.Config.OpenIM.Ip, fmt.Sprintf("%d", config.Config.OpenIM.ApiPort)))
resp, err := auth.GetUserToken(param, "getUserIMToken", host)
if err != nil </span><span class="cov0" title="0">{
return &response.TokenData{}, err
}</span>
<span class="cov0" title="0">if resp.ErrCode != 0 </span><span class="cov0" title="0">{
return &response.TokenData{}, errors.New(resp.ErrMsg)
}</span>
<span class="cov0" title="0">return &resp.Data, nil</span>
}
// GetAdminToken get admin token.
func (svc *UserService) GetAdminToken() (*response.TokenData, error) <span class="cov0" title="0">{
params := &request.UserTokenParams{
Secret: config.Config.OpenIM.Secret,
PlatformID: uint(config.Config.OpenIM.PlatformID),
UserID: config.Config.OpenIM.AdminID,
}
// TODO: Get cache from redis
return getUserIMToken(params)
}</span>
// GetUserInfoByUUID get user info by uuid.
func (svc *UserService) GetUserInfoByUUID(uid string) (*responseparams.UserInfoResponse, error) <span class="cov0" title="0">{
resp := &responseparams.UserInfoResponse{}
if uid == "" </span><span class="cov0" title="0">{
return resp, common.NewError(common.I_INVALID_PARAM)
}</span>
<span class="cov0" title="0">u, err := svc.SysUserDao.FindFirstByUUID(uid)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
<span class="cov0" title="0">resp.UUID = u.UUID
resp.Email = u.Email
resp.Nickname = u.Nickname
resp.Avatar = u.Avatar
resp.Description = u.Description
resp.IsAdmin = u.IsAdmin
resp.IsEnable = u.IsEnable
resp.CreatedAt = u.CreatedAt.Format("2006-01-02 15:04:05")
return resp, nil</span>
}
// UpdateUserInfo update user info.
func (svc *UserService) UpdateUserInfo(uid string, params *requestparams.UpdateUserInfoParams) (*responseparams.UserInfoResponse, error) <span class="cov0" title="0">{
resp := &responseparams.UserInfoResponse{}
if uid == "" </span><span class="cov0" title="0">{
return resp, common.NewError(common.I_INVALID_PARAM)
}</span>
<span class="cov0" title="0">u, err := svc.SysUserDao.FindFirstByUUID(uid)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
// Update user info
<span class="cov0" title="0">if params.Nickname != nil </span><span class="cov0" title="0">{
u.Nickname = *params.Nickname
}</span>
<span class="cov0" title="0">if params.Description != nil </span><span class="cov0" title="0">{
u.Description = *params.Description
}</span>
<span class="cov0" title="0">if params.Avatar != nil </span><span class="cov0" title="0">{
u.Avatar = *params.Avatar
}</span>
<span class="cov0" title="0">if params.Email != nil </span><span class="cov0" title="0">{
u.Email = *params.Email
}</span>
// TODO: Check if the user is admin
<span class="cov0" title="0">if params.IsEnable != nil </span><span class="cov0" title="0">{
u.IsEnable = *params.IsEnable
}</span>
<span class="cov0" title="0">if params.IsAdmin != nil </span><span class="cov0" title="0">{
u.IsAdmin = *params.IsAdmin
}</span>
// Update user info
<span class="cov0" title="0">if err = svc.SysUserDao.Update(u); err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
// Get new info
<span class="cov0" title="0">u, _ = svc.SysUserDao.FindFirstByUUID(uid)
resp.UUID = u.UUID
resp.Email = u.Email
resp.Nickname = u.Nickname
resp.Avatar = u.Avatar
resp.Description = u.Description
resp.IsAdmin = u.IsAdmin
resp.IsEnable = u.IsEnable
resp.CreatedAt = u.CreatedAt.Format("2006-01-02 15:04:05")
return resp, nil</span>
}
// UpdateUserPassword update user password.
func (svc *UserService) UpdateUserPassword(uid string, params *requestparams.UpdateUserPasswordParams) error <span class="cov0" title="0">{
if uid == "" </span><span class="cov0" title="0">{
return common.NewError(common.I_INVALID_PARAM)
}</span>
<span class="cov0" title="0">u, err := svc.SysUserDao.FindFirstByUUID(uid)
if err != nil </span><span class="cov0" title="0">{
return err
}</span>
// Update user info
<span class="cov0" title="0">if params.Password != "" && params.RepeatPassword != "" &&
params.Password == params.RepeatPassword </span><span class="cov0" title="0">{
u.Password = utils.EncryptPassword(params.Password)
}</span>
// Update user info
<span class="cov0" title="0">if err = svc.SysUserDao.Update(u); err != nil </span><span class="cov0" title="0">{
return err
}</span>
<span class="cov0" title="0">return nil</span>
}
// GetCommunityUserList get community user list.
func (svc *UserService) GetCommunityUserList(cid string, params *requestparams.ListPageParams) (*responseparams.ListPageResponse, error) <span class="cov0" title="0">{
resp := &responseparams.ListPageResponse{}
userInfos := make([]*responseparams.UserInfoResponse, 0)
csvc := NewCommunityService((svc.ctx).(*gin.Context))
info, err := csvc.GetCommunityInfoByUUIDV2(cid)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
// Get user list
<span class="cov0" title="0">users, total, err := svc.SysUserDao.FindByCommunityIdPage(info.Id, (params.Page-1)*params.PageSize, params.PageSize)
if err != nil </span><span class="cov0" title="0">{
return resp, err
}</span>
// Fill response data
<span class="cov0" title="0">for _, u := range users </span><span class="cov0" title="0">{
userInfos = append(userInfos, &responseparams.UserInfoResponse{
UUID: u.UUID,
Email: u.Email,
Nickname: u.Nickname,
Avatar: u.Avatar,
Description: u.Description,
IsAdmin: u.IsAdmin,
IsEnable: u.IsEnable,
CreatedAt: u.CreatedAt.Format("2006-01-02 15:04:05"),
})
}</span>
<span class="cov0" title="0">resp.List = userInfos
resp.Page = params.Page
resp.PageSize = params.PageSize
resp.Total = int(total)
return resp, nil</span>
}
</pre>
<pre class="file" id="file10" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package service
import (
"context"
"errors"
"github.com/shomali11/slacker"
"github.com/OpenIMSDK/OpenKF/server/internal/dal/dao"
)
// user dispatch queue key.
const (
USER_DISPATCH_QUEUE_KEY = "openkf:user_dispatch_queue"
USER_SLACK_MAP_KEY = "openkf:user_slack_map"
)
// UserDispatchService user service.
type UserDispatchService struct {
Service
UserDispatchDao *dao.UserDispatchDao
SysUserDao *dao.SysUserDao
}
// NewUserDispatchService return new service with context.
func NewUserDispatchService(c context.Context) *UserDispatchService <span class="cov8" title="1">{
return &UserDispatchService{
Service: Service{
ctx: c,
},
UserDispatchDao: dao.NewUserDispatchDao(),
SysUserDao: dao.NewSysUserDao(),
}
}</span>
// AddUser add user to enqueue.
func (s *UserDispatchService) AddUser(uuid string) error <span class="cov0" title="0">{
_, err := s.UserDispatchDao.AddUser(USER_DISPATCH_QUEUE_KEY, uuid)
return err
}</span>
// GetUser get user and update timestamp.
func (s *UserDispatchService) GetUser() (string, error) <span class="cov8" title="1">{
res, err := s.UserDispatchDao.GetUser(USER_DISPATCH_QUEUE_KEY)
if err != nil </span><span class="cov0" title="0">{
return "", errors.New("can not find a user")
}</span>
// return a default value if res is empty
<span class="cov8" title="1">if res == "" </span><span class="cov0" title="0">{
u, err := s.SysUserDao.First()
if err != nil </span><span class="cov0" title="0">{
return "", err
}</span>
<span class="cov0" title="0">return u.UUID, nil</span>
}
<span class="cov8" title="1">return res, nil</span>
}
// DeleteUser delete user from queue.
func (s *UserDispatchService) DeleteUser(uuid string) error <span class="cov0" title="0">{
_, err := s.UserDispatchDao.RemoveUser(USER_DISPATCH_QUEUE_KEY, uuid)
return err
}</span>
// SetSlackMap set slack map.
func (s *UserDispatchService) SetSlackMap(customID, staffID string, botContext slacker.BotContext) error <span class="cov0" title="0">{
return s.UserDispatchDao.SetSlackMap(USER_SLACK_MAP_KEY, customID, staffID, botContext.Event().ChannelID)
}</span>
// GetSlackMap get slack map.
func (s *UserDispatchService) GetSlackMap(customID string) *dao.SlackMap <span class="cov0" title="0">{
return s.UserDispatchDao.GetSlackMap(USER_SLACK_MAP_KEY, customID)
}</span>
// GetSlackIDs get all staff id.
func (s *UserDispatchService) GetSlackIDs() ([]string, error) <span class="cov0" title="0">{
return s.UserDispatchDao.GetSlackIDs(USER_SLACK_MAP_KEY)
}</span>
// SlackUserFilter filter user by slack id.
func (s *UserDispatchService) SlackUserFilter(uid string) bool <span class="cov0" title="0">{
ids, err := s.GetSlackIDs()
if err != nil || len(ids) == 0 </span><span class="cov0" title="0">{
return false
}</span>
<span class="cov0" title="0">for _, id := range ids </span><span class="cov0" title="0">{
if id == uid </span><span class="cov0" title="0">{
return true
}</span>
}
<span class="cov0" title="0">return false</span>
}
</pre>
<pre class="file" id="file11" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"fmt"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/client"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/response"
)
const (
// PATH_USER_TOKEN get user token path.
PATH_USER_TOKEN = "/auth/user_token"
)
// GetUserToken get user token from openim server.
func GetUserToken(param *request.UserTokenParams, operationID, host string) (*response.UserTokenResponse, error) <span class="cov8" title="1">{
// host: http://ip:port
url := fmt.Sprintf("%s%s", host, PATH_USER_TOKEN)
r := &response.UserTokenResponse{}
client := client.NewClient(url)
resp, err := client.POST(operationID, "", param)
if err != nil </span><span class="cov0" title="0">{
return r, err
}</span>
<span class="cov8" title="1">r.ErrCode = uint(resp["errCode"].(float64))
r.ErrMsg = resp["errMsg"].(string)
r.ErrDlt = resp["errDlt"].(string)
if data, ok := resp["data"]; ok </span><span class="cov8" title="1">{
data := data.(map[string]interface{})
r.Data.Token = data["token"].(string)
r.Data.ExpireTimeSeconds = uint(data["expireTimeSeconds"].(float64))
}</span>
<span class="cov8" title="1">return r, nil</span>
}
</pre>
<pre class="file" id="file12" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package msg
import (
"errors"
"fmt"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/client"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/response"
)
const (
// PATH_SEND_MSG admin send message.
PATH_SEND_MSG = "/msg/send_msg"
)
// AdminSendMsg admin send message.
func AdminSendMsg(param *request.MsgInfo, operationID, host, adminToken string) (*response.BaseResponse, error) <span class="cov8" title="1">{
// host: http://ip:port
url := fmt.Sprintf("%s%s", host, PATH_SEND_MSG)
r := &response.BaseResponse{}
client := client.NewClient(url)
resp, err := client.POST(operationID, adminToken, param)
if err != nil </span><span class="cov0" title="0">{
return r, err
}</span>
<span class="cov8" title="1">r.ErrCode = uint(resp["errCode"].(float64))
r.ErrMsg = resp["errMsg"].(string)
r.ErrDlt = resp["errDlt"].(string)
if resp["data"] == nil </span><span class="cov8" title="1">{
return r, errors.New("data is nil: " + r.ErrMsg)
}</span>
<span class="cov0" title="0">r.Data = resp["data"]
return r, nil</span>
}
</pre>
<pre class="file" id="file13" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package user
import (
"fmt"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/client"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/request"
"github.com/OpenIMSDK/OpenKF/server/pkg/openim/param/response"
)
const (
// PATH_USER_REGISTER register user path.
PATH_USER_REGISTER = "/user/user_register"
)
// RegisterUser register user.
func RegisterUser(param *request.RegisterUserParams, operationID, host string) (*response.BaseResponse, error) <span class="cov8" title="1">{
// host: http://ip:port
url := fmt.Sprintf("%s%s", host, PATH_USER_REGISTER)
r := &response.BaseResponse{}
client := client.NewClient(url)
resp, err := client.POST(operationID, "", param)
if err != nil </span><span class="cov0" title="0">{
return r, err
}</span>
<span class="cov8" title="1">r.ErrCode = uint(resp["errCode"].(float64))
r.ErrMsg = resp["errMsg"].(string)
r.ErrDlt = resp["errDlt"].(string)
return r, nil</span>
}
</pre>
<pre class="file" id="file14" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package utils
import (
"crypto/hmac"
"crypto/md5"
"crypto/sha256"
"encoding/hex"
)
const (
_salt = "openkf"
)
// EncryptPassword encrypt password.
func EncryptPassword(password string) string <span class="cov8" title="1">{
// md5
m := md5.New()
m.Write([]byte(password + _salt))
mByte := m.Sum(nil)
// hmac
h := hmac.New(sha256.New, []byte(_salt))
h.Write(mByte)
password = hex.EncodeToString(h.Sum(nil))
return password
}</span>
// ComparePassword compare password.
func ComparePassword(password, encryptedPassword string) bool <span class="cov8" title="1">{
return EncryptPassword(password) == encryptedPassword
}</span>
</pre>
<pre class="file" id="file15" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package utils
import "strconv"
// IntToString convert int to string.
func IntToString(i interface{}) string <span class="cov8" title="1">{
return strconv.FormatInt(int64(i.(int)), 10)
}</span>
// StringToInt convert string to int.
func StringToInt(i string) int <span class="cov8" title="1">{
j, _ := strconv.Atoi(i)
return j
}</span>
</pre>
<pre class="file" id="file16" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package utils
import (
"fmt"
"github.com/gofrs/uuid"
)
// GenUUID generate uuid.
func GenUUID() string <span class="cov8" title="1">{
return uuid.Must(uuid.NewV4()).String()
}</span>
// GenUUIDWithoutHyphen generate uuid without hyphen.
func GenUUIDWithoutHyphen() string <span class="cov8" title="1">{
return toString(uuid.Must(uuid.NewV4()))
}</span>
// encodeCanonical encodes the canonical RFC-4122 form of UUID u into the
// first 36 bytes dst.
func encodeCanonical(dst []byte, u uuid.UUID) <span class="cov8" title="1">{
const hextable = "0123456789abcdef"
dst[8] = '-'
dst[13] = '-'
dst[18] = '-'
dst[23] = '-'
for i, x := range [16]byte{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34,
} </span><span class="cov8" title="1">{
c := u[i]
dst[x] = hextable[c>>4]
dst[x+1] = hextable[c&0x0f]
}</span>
}
// String returns a canonical RFC-4122 string representation of the UUID:
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, but delete separator -.
func toString(u uuid.UUID) string <span class="cov8" title="1">{
var buf [36]byte
encodeCanonical(buf[:], u)
return fmt.Sprintf("%s%s%s%s%s", buf[0:7], buf[9:12], buf[14:17], buf[19:22], buf[24:])
}</span>
</pre>
<pre class="file" id="file17" style="display: none">// Copyright © 2023 OpenIM open source community. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package utils
import "regexp"
// IsValidEmail check if the email is valid.
func IsValidEmail(email string) bool <span class="cov8" title="1">{
// Regular expression for basic email validation
// This regex pattern is a simplified version and may not cover all edge cases.
// You can use more comprehensive patterns depending on your specific needs.
emailRegex := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
return regexp.MustCompile(emailRegex).MatchString(email)
}</span>
</pre>
</div>
</body>
<script>
(function() {
var files = document.getElementById('files');
var visible;
files.addEventListener('change', onChange, false);
function select(part) {
if (visible)
visible.style.display = 'none';
visible = document.getElementById(part);
if (!visible)
return;
files.value = part;
visible.style.display = 'block';
location.hash = part;
}
function onChange() {
select(files.value);
window.scrollTo(0, 0);
}
if (location.hash != "") {
select(location.hash.substr(1));
}
if (!visible) {
select("file0");
}
})();
</script>
</html> |
cubxxw
approved these changes
Sep 7, 2023
IRONICBo
approved these changes
Sep 7, 2023
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
🔍 What type of PR is this?
Enhance system.
Add one of the following kinds:
/kind feature
Optionally add one or more of the following kinds if applicable:
/kind failing-test
👀 What this PR does / why we need it:
🅰 Which issue(s) this PR fixes:
Fix #117
📝 Special notes for your reviewer:
🎯 Describe how to verify it
https://github.com/a67793581/OpenKF/tree/feat/test/server/test
📑 Additional documentation e.g., RFC, notion, Google docs, usage docs, etc.:
https://github.com/a67793581/OpenKF/tree/feat/test/server/test