mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-08 10:46:37 +08:00
feat: add Go backend compatibility service scaffold
This commit is contained in:
@@ -0,0 +1,420 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user