mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-07 02:06:38 +08:00
421 lines
10 KiB
Go
421 lines
10 KiB
Go
package sqlite
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"time"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
type Repository struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
type User struct {
|
|
ID int64
|
|
User string
|
|
Pwd string
|
|
RoleID int
|
|
ExpTime int64
|
|
Flow int64
|
|
InFlow int64
|
|
OutFlow int64
|
|
FlowResetTime int64
|
|
Num int
|
|
CreatedTime int64
|
|
UpdatedTime sql.NullInt64
|
|
Status int
|
|
}
|
|
|
|
type ViteConfig struct {
|
|
ID int64 `json:"id"`
|
|
Name string `json:"name"`
|
|
Value string `json:"value"`
|
|
Time int64 `json:"time"`
|
|
}
|
|
|
|
type UserTunnelDetail struct {
|
|
ID int64
|
|
UserID int64
|
|
TunnelID int64
|
|
TunnelName string
|
|
TunnelFlow int
|
|
Flow int64
|
|
InFlow int64
|
|
OutFlow int64
|
|
Num int
|
|
FlowResetTime int64
|
|
ExpTime int64
|
|
SpeedID sql.NullInt64
|
|
SpeedLimit sql.NullString
|
|
Speed sql.NullInt64
|
|
}
|
|
|
|
type UserForwardDetail struct {
|
|
ID int64
|
|
Name string
|
|
TunnelID int64
|
|
TunnelName string
|
|
InIP string
|
|
InPort sql.NullInt64
|
|
RemoteAddr string
|
|
InFlow int64
|
|
OutFlow int64
|
|
Status int
|
|
CreatedAt int64
|
|
}
|
|
|
|
type StatisticsFlow struct {
|
|
ID int64 `json:"id"`
|
|
UserID int64 `json:"userId"`
|
|
Flow int64 `json:"flow"`
|
|
TotalFlow int64 `json:"totalFlow"`
|
|
Time string `json:"time"`
|
|
}
|
|
|
|
type Node struct {
|
|
ID int64
|
|
Secret string
|
|
Version sql.NullString
|
|
HTTP int
|
|
TLS int
|
|
Socks int
|
|
Status int
|
|
}
|
|
|
|
func Open(path string) (*Repository, error) {
|
|
db, err := sql.Open("sqlite", path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := db.Ping(); err != nil {
|
|
_ = db.Close()
|
|
return nil, err
|
|
}
|
|
|
|
return &Repository{db: db}, nil
|
|
}
|
|
|
|
func (r *Repository) Close() error {
|
|
if r == nil || r.db == nil {
|
|
return nil
|
|
}
|
|
return r.db.Close()
|
|
}
|
|
|
|
func (r *Repository) GetUserByUsername(username string) (*User, error) {
|
|
if r == nil || r.db == nil {
|
|
return nil, errors.New("repository not initialized")
|
|
}
|
|
|
|
row := r.db.QueryRow(`
|
|
SELECT id, user, pwd, role_id, exp_time, flow, in_flow, out_flow, flow_reset_time, num, created_time, updated_time, status
|
|
FROM user WHERE user = ? LIMIT 1
|
|
`, username)
|
|
user := &User{}
|
|
if err := row.Scan(
|
|
&user.ID, &user.User, &user.Pwd, &user.RoleID, &user.ExpTime,
|
|
&user.Flow, &user.InFlow, &user.OutFlow, &user.FlowResetTime,
|
|
&user.Num, &user.CreatedTime, &user.UpdatedTime, &user.Status,
|
|
); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func (r *Repository) GetConfigByName(name string) (*ViteConfig, error) {
|
|
if r == nil || r.db == nil {
|
|
return nil, errors.New("repository not initialized")
|
|
}
|
|
|
|
row := r.db.QueryRow(`SELECT id, name, value, time FROM vite_config WHERE name = ? LIMIT 1`, name)
|
|
cfg := &ViteConfig{}
|
|
if err := row.Scan(&cfg.ID, &cfg.Name, &cfg.Value, &cfg.Time); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return cfg, nil
|
|
}
|
|
|
|
func (r *Repository) ListConfigs() (map[string]string, error) {
|
|
if r == nil || r.db == nil {
|
|
return nil, errors.New("repository not initialized")
|
|
}
|
|
|
|
rows, err := r.db.Query(`SELECT name, value FROM vite_config`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
result := make(map[string]string)
|
|
for rows.Next() {
|
|
var name, value string
|
|
if err := rows.Scan(&name, &value); err != nil {
|
|
return nil, err
|
|
}
|
|
result[name] = value
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (r *Repository) UpsertConfig(name, value string, now int64) error {
|
|
if r == nil || r.db == nil {
|
|
return errors.New("repository not initialized")
|
|
}
|
|
|
|
_, err := r.db.Exec(`
|
|
INSERT INTO vite_config(name, value, time)
|
|
VALUES(?, ?, ?)
|
|
ON CONFLICT(name) DO UPDATE SET value=excluded.value, time=excluded.time
|
|
`, name, value, now)
|
|
return err
|
|
}
|
|
|
|
func (r *Repository) GetUserByID(id int64) (*User, error) {
|
|
if r == nil || r.db == nil {
|
|
return nil, errors.New("repository not initialized")
|
|
}
|
|
|
|
row := r.db.QueryRow(`
|
|
SELECT id, user, pwd, role_id, exp_time, flow, in_flow, out_flow, flow_reset_time, num, created_time, updated_time, status
|
|
FROM user WHERE id = ? LIMIT 1
|
|
`, id)
|
|
user := &User{}
|
|
if err := row.Scan(
|
|
&user.ID, &user.User, &user.Pwd, &user.RoleID, &user.ExpTime,
|
|
&user.Flow, &user.InFlow, &user.OutFlow, &user.FlowResetTime,
|
|
&user.Num, &user.CreatedTime, &user.UpdatedTime, &user.Status,
|
|
); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func (r *Repository) UsernameExistsExceptID(username string, exceptID int64) (bool, error) {
|
|
if r == nil || r.db == nil {
|
|
return false, errors.New("repository not initialized")
|
|
}
|
|
|
|
row := r.db.QueryRow(`SELECT COUNT(1) FROM user WHERE user = ? AND id != ?`, username, exceptID)
|
|
var count int
|
|
if err := row.Scan(&count); err != nil {
|
|
return false, err
|
|
}
|
|
return count > 0, nil
|
|
}
|
|
|
|
func (r *Repository) UpdateUserNameAndPassword(userID int64, username, passwordMD5 string, now int64) error {
|
|
if r == nil || r.db == nil {
|
|
return errors.New("repository not initialized")
|
|
}
|
|
_, err := r.db.Exec(`UPDATE user SET user = ?, pwd = ?, updated_time = ? WHERE id = ?`, username, passwordMD5, now, userID)
|
|
return err
|
|
}
|
|
|
|
func (r *Repository) GetUserPackageTunnels(userID int64) ([]UserTunnelDetail, error) {
|
|
if r == nil || r.db == nil {
|
|
return nil, errors.New("repository not initialized")
|
|
}
|
|
|
|
rows, err := r.db.Query(`
|
|
SELECT ut.id, ut.user_id, ut.tunnel_id, t.name, t.flow, ut.flow, ut.in_flow, ut.out_flow,
|
|
ut.num, ut.flow_reset_time, ut.exp_time, ut.speed_id, sl.name, sl.speed
|
|
FROM user_tunnel ut
|
|
LEFT JOIN tunnel t ON t.id = ut.tunnel_id
|
|
LEFT JOIN speed_limit sl ON sl.id = ut.speed_id
|
|
WHERE ut.user_id = ?
|
|
ORDER BY ut.id ASC
|
|
`, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
items := make([]UserTunnelDetail, 0)
|
|
for rows.Next() {
|
|
var item UserTunnelDetail
|
|
if err := rows.Scan(
|
|
&item.ID, &item.UserID, &item.TunnelID, &item.TunnelName, &item.TunnelFlow,
|
|
&item.Flow, &item.InFlow, &item.OutFlow, &item.Num, &item.FlowResetTime,
|
|
&item.ExpTime, &item.SpeedID, &item.SpeedLimit, &item.Speed,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return items, nil
|
|
}
|
|
|
|
func (r *Repository) GetUserPackageForwards(userID int64) ([]UserForwardDetail, error) {
|
|
if r == nil || r.db == nil {
|
|
return nil, errors.New("repository not initialized")
|
|
}
|
|
|
|
rows, err := r.db.Query(`
|
|
SELECT f.id, f.name, f.tunnel_id, t.name, f.remote_addr, f.in_flow, f.out_flow, f.status, f.created_time,
|
|
GROUP_CONCAT(n.server_ip || ':' || fp.port), MIN(fp.port)
|
|
FROM forward f
|
|
LEFT JOIN tunnel t ON t.id = f.tunnel_id
|
|
LEFT JOIN forward_port fp ON fp.forward_id = f.id
|
|
LEFT JOIN node n ON n.id = fp.node_id
|
|
WHERE f.user_id = ?
|
|
GROUP BY f.id
|
|
ORDER BY f.id ASC
|
|
`, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
items := make([]UserForwardDetail, 0)
|
|
for rows.Next() {
|
|
var item UserForwardDetail
|
|
if err := rows.Scan(
|
|
&item.ID, &item.Name, &item.TunnelID, &item.TunnelName, &item.RemoteAddr,
|
|
&item.InFlow, &item.OutFlow, &item.Status, &item.CreatedAt, &item.InIP, &item.InPort,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return items, nil
|
|
}
|
|
|
|
func (r *Repository) GetStatisticsFlows(userID int64, limit int) ([]StatisticsFlow, error) {
|
|
if r == nil || r.db == nil {
|
|
return nil, errors.New("repository not initialized")
|
|
}
|
|
|
|
rows, err := r.db.Query(`
|
|
SELECT id, user_id, flow, total_flow, time
|
|
FROM statistics_flow
|
|
WHERE user_id = ?
|
|
ORDER BY id DESC
|
|
LIMIT ?
|
|
`, userID, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
items := make([]StatisticsFlow, 0)
|
|
for rows.Next() {
|
|
var item StatisticsFlow
|
|
if err := rows.Scan(&item.ID, &item.UserID, &item.Flow, &item.TotalFlow, &item.Time); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return items, nil
|
|
}
|
|
|
|
func (r *Repository) NodeExistsBySecret(secret string) (bool, error) {
|
|
if r == nil || r.db == nil {
|
|
return false, errors.New("repository not initialized")
|
|
}
|
|
|
|
row := r.db.QueryRow(`SELECT COUNT(1) FROM node WHERE secret = ?`, secret)
|
|
var count int
|
|
if err := row.Scan(&count); err != nil {
|
|
return false, err
|
|
}
|
|
return count > 0, nil
|
|
}
|
|
|
|
func (r *Repository) GetNodeBySecret(secret string) (*Node, error) {
|
|
if r == nil || r.db == nil {
|
|
return nil, errors.New("repository not initialized")
|
|
}
|
|
|
|
row := r.db.QueryRow(`SELECT id, secret, version, http, tls, socks, status FROM node WHERE secret = ? LIMIT 1`, secret)
|
|
var n Node
|
|
if err := row.Scan(&n.ID, &n.Secret, &n.Version, &n.HTTP, &n.TLS, &n.Socks, &n.Status); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return &n, nil
|
|
}
|
|
|
|
func (r *Repository) UpdateNodeOnline(nodeID int64, status int, version string, httpVal, tlsVal, socksVal int) error {
|
|
if r == nil || r.db == nil {
|
|
return errors.New("repository not initialized")
|
|
}
|
|
_, err := r.db.Exec(`UPDATE node SET status = ?, version = ?, http = ?, tls = ?, socks = ?, updated_time = ? WHERE id = ?`,
|
|
status, version, httpVal, tlsVal, socksVal, unixMilliNow(), nodeID)
|
|
return err
|
|
}
|
|
|
|
func (r *Repository) UpdateNodeStatus(nodeID int64, status int) error {
|
|
if r == nil || r.db == nil {
|
|
return errors.New("repository not initialized")
|
|
}
|
|
_, err := r.db.Exec(`UPDATE node SET status = ?, updated_time = ? WHERE id = ?`, status, unixMilliNow(), nodeID)
|
|
return err
|
|
}
|
|
|
|
func (r *Repository) AddFlow(forwardID, userID int64, userTunnelID int64, inFlow, outFlow int64) error {
|
|
if r == nil || r.db == nil {
|
|
return errors.New("repository not initialized")
|
|
}
|
|
|
|
tx, err := r.db.Begin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
_ = tx.Rollback()
|
|
}
|
|
}()
|
|
|
|
if _, err = tx.Exec(`UPDATE forward SET in_flow = in_flow + ?, out_flow = out_flow + ? WHERE id = ?`, inFlow, outFlow, forwardID); err != nil {
|
|
return err
|
|
}
|
|
if _, err = tx.Exec(`UPDATE user SET in_flow = in_flow + ?, out_flow = out_flow + ? WHERE id = ?`, inFlow, outFlow, userID); err != nil {
|
|
return err
|
|
}
|
|
if userTunnelID > 0 {
|
|
if _, err = tx.Exec(`UPDATE user_tunnel SET in_flow = in_flow + ?, out_flow = out_flow + ? WHERE id = ?`, inFlow, outFlow, userTunnelID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
err = tx.Commit()
|
|
return err
|
|
}
|
|
|
|
func unixMilliNow() int64 {
|
|
return time.Now().UnixMilli()
|
|
}
|