func (l *Logic) GetUserKey(uid string) ([]byte, error) {
	u, err := l.db.GetUser(uid)
	if err != nil {
		return nil, err
	}
	return u.SecretKey, nil
}
GetUserKey find user key with user id
package logic
import (
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/sha1"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"time"
	"github.com/chanify/chanify/crypto"
	"github.com/chanify/chanify/model"
	"github.com/google/uuid"
	"github.com/sideshow/apns2"
	"github.com/sideshow/apns2/payload"
	"github.com/sideshow/apns2/token"
)
var (
	APIEndpoint            = "https://api.chanify.net"
	MockPusher  APNSPusher = nil
	randReader = rand.Read
	ErrNoSupportMethod = errors.New("no support method")
	ErrNotFound        = errors.New("not found")
	ErrInvalidContent  = errors.New("invalid content")
	ErrSystemLimited   = errors.New("system limited")
)
type Options struct {
	Name         string
	Version      string
	Endpoint     string
	DataPath     string
	FilePath     string
	DBUrl        string
	Secret       string
	Registerable bool
	RegUsers     []string
}
type Logic struct {
	srvless      bool
	registerable bool
	db           model.DB
	secKey       *crypto.SecretKey
	Name         string
	NodeID       string
	Version      string
	Endpoint     string
	Features     []string
	infoData    []byte
	infoSign    string
	whitelist   map[string]bool
	filepath    string
	apnsPClient *apns2.Client
	apnsDClient *apns2.Client
}
type APNSPusher interface {
	Push(n *apns2.Notification) (*apns2.Response, error)
}
const authKey = "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgQ6vCLkUeDj223nfPfKGrjG+Coc53EbKHmO6Oa9YcHiGgCgYIKoZIzj0DAQehRANCAAQNwg3W2eOqNlX0nl9kGbfmMxwSZoO4RmqKoKJnH/vGkU8csJuN5Dg4JiI6ni5PEx+A1rb19DuDm4AzwBVvl8Jt"
func (opts *Options) fixOptions() {
	if len(opts.DataPath) > 0 {
		s, err := os.Stat(opts.DataPath)
		if err == nil && s.IsDir() {
			if len(opts.Secret) <= 0 && len(opts.DBUrl) <= 0 {
				opts.DBUrl = "sqlite://" + filepath.Join(opts.DataPath, "chanify.db")
			}
			if len(opts.FilePath) <= 0 {
				opts.FilePath = filepath.Join(opts.DataPath, "files")
			}
		}
	}
}
func NewLogic(opts *Options) (*Logic, error) {
	opts.fixOptions()
	l := &Logic{
		srvless:      false,
		registerable: opts.Registerable,
		Name:         opts.Name,
		Version:      opts.Version,
		Endpoint:     opts.Endpoint,
		Features:     []string{"platform.watchos", "msg.text", "msg.link", "msg.action"},
	}
	if l.registerable {
		log.Println("Register user enabled")
	} else {
		log.Println("Register user disabled")
		whitelist := map[string]bool{}
		for _, u := range opts.RegUsers {
			whitelist[u] = true
		}
		l.whitelist = whitelist
		log.Println("Find", len(whitelist), "user(s) in whitelist")
		l.Features = append([]string{"register.limit"}, l.Features...)
	}
	if len(opts.DBUrl) <= 0 {
		if len(opts.Secret) > 0 {
			opts.DBUrl = "nosql://?secret=" + url.QueryEscape(opts.Secret)
			l.srvless = true
		} else {
			return nil, errors.New("in serverless mode, secret is required")
		}
	}
	if err := l.loadDB(opts.DBUrl); err != nil {
		return nil, err
	}
	l.NodeID = l.secKey.ToID(0x01)
	if !l.srvless {
		l.Features = append([]string{"store.device"}, l.Features...)
		key, _ := base64.RawStdEncoding.DecodeString(authKey)
		akey, _ := x509.ParsePKCS8PrivateKey(key)
		tk := &token.Token{
			AuthKey: akey.(*ecdsa.PrivateKey),
			KeyID:   "CPBF9RLA6G",
			TeamID:  "P4XS4AVCLW",
		}
		l.filepath = opts.FilePath
		l.apnsPClient = apns2.NewTokenClient(tk).Production()
		l.apnsDClient = apns2.NewTokenClient(tk).Development()
		if len(l.filepath) > 0 {
			l.Features = append(l.Features, "msg.image", "msg.audio", "msg.file", "msg.timeline")
			fixPath(filepath.Join(l.filepath, "images"))  
			fixPath(filepath.Join(l.filepath, "audios"))  
			fixPath(filepath.Join(l.filepath, "files"))   
			log.Println("Files path:", l.filepath)
		}
	}
	l.InitInfo()
	log.Printf("Node server name: %s, version: %s, serverless: %v, node-id: %s\n", l.Name, l.Version, l.srvless, l.NodeID)
	return l, nil
}
func (l *Logic) Close() {
	if l.db != nil {
		l.db.Close()
		l.db = nil
	}
}
func (l *Logic) CanFileStore() bool {
	return len(l.filepath) > 0
}
func (l *Logic) GetUser(uid string) (*model.User, error) {
	return l.db.GetUser(uid)
}