mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-06-03 01:37:30 +03:00
Add admin panel, manager, node_manager, bandwidth limiter, connection limiter, bonding, failover, vless encryption, mkcp transport
This commit is contained in:
400
service/admin_panel/migration/postgresql.go
Normal file
400
service/admin_panel/migration/postgresql.go
Normal file
@@ -0,0 +1,400 @@
|
||||
package migration
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
"github.com/golang-migrate/migrate/v4/database/postgres"
|
||||
"github.com/sagernet/sing-box/common/migrate/source"
|
||||
)
|
||||
|
||||
var migrations = map[string]string{
|
||||
"1_initialize_schema.up.sql": `
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
SET idle_in_transaction_session_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = on;
|
||||
SELECT pg_catalog.set_config('search_path', '', false);
|
||||
SET check_function_bodies = false;
|
||||
SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
CREATE SEQUENCE public.goadmin_menu_myid_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
MAXVALUE 99999999
|
||||
CACHE 1;
|
||||
|
||||
|
||||
SET default_tablespace = '';
|
||||
|
||||
SET default_table_access_method = heap;
|
||||
|
||||
CREATE TABLE public.goadmin_menu (
|
||||
id integer DEFAULT nextval('public.goadmin_menu_myid_seq'::regclass) NOT NULL,
|
||||
parent_id integer DEFAULT 0 NOT NULL,
|
||||
type integer DEFAULT 0,
|
||||
"order" integer DEFAULT 0 NOT NULL,
|
||||
title character varying(50) NOT NULL,
|
||||
header character varying(100),
|
||||
plugin_name character varying(100) NOT NULL,
|
||||
icon character varying(50) NOT NULL,
|
||||
uri character varying(3000) NOT NULL,
|
||||
uuid character varying(100),
|
||||
created_at timestamp without time zone DEFAULT now(),
|
||||
updated_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.goadmin_operation_log_myid_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
MAXVALUE 99999999
|
||||
CACHE 1;
|
||||
|
||||
CREATE TABLE public.goadmin_operation_log (
|
||||
id integer DEFAULT nextval('public.goadmin_operation_log_myid_seq'::regclass) NOT NULL,
|
||||
user_id integer NOT NULL,
|
||||
path character varying(255) NOT NULL,
|
||||
method character varying(10) NOT NULL,
|
||||
ip character varying(15) NOT NULL,
|
||||
input text NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now(),
|
||||
updated_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.goadmin_permissions_myid_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
MAXVALUE 99999999
|
||||
CACHE 1;
|
||||
|
||||
CREATE TABLE public.goadmin_permissions (
|
||||
id integer DEFAULT nextval('public.goadmin_permissions_myid_seq'::regclass) NOT NULL,
|
||||
name character varying(50) NOT NULL,
|
||||
slug character varying(50) NOT NULL,
|
||||
http_method character varying(255),
|
||||
http_path text NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now(),
|
||||
updated_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE public.goadmin_role_menu (
|
||||
role_id integer NOT NULL,
|
||||
menu_id integer NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now(),
|
||||
updated_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE public.goadmin_role_permissions (
|
||||
role_id integer NOT NULL,
|
||||
permission_id integer NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now(),
|
||||
updated_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE public.goadmin_role_users (
|
||||
role_id integer NOT NULL,
|
||||
user_id integer NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now(),
|
||||
updated_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.goadmin_roles_myid_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
MAXVALUE 99999999
|
||||
CACHE 1;
|
||||
|
||||
CREATE TABLE public.goadmin_roles (
|
||||
id integer DEFAULT nextval('public.goadmin_roles_myid_seq'::regclass) NOT NULL,
|
||||
name character varying NOT NULL,
|
||||
slug character varying NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now(),
|
||||
updated_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.goadmin_session_myid_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
MAXVALUE 99999999
|
||||
CACHE 1;
|
||||
|
||||
CREATE TABLE public.goadmin_session (
|
||||
id integer DEFAULT nextval('public.goadmin_session_myid_seq'::regclass) NOT NULL,
|
||||
sid character varying(50) NOT NULL,
|
||||
"values" character varying(3000) NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now(),
|
||||
updated_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.goadmin_site_myid_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
MAXVALUE 99999999
|
||||
CACHE 1;
|
||||
|
||||
CREATE TABLE public.goadmin_site (
|
||||
id integer DEFAULT nextval('public.goadmin_site_myid_seq'::regclass) NOT NULL,
|
||||
key character varying(100) NOT NULL,
|
||||
value text NOT NULL,
|
||||
type integer DEFAULT 0,
|
||||
description character varying(3000),
|
||||
state integer DEFAULT 0,
|
||||
created_at timestamp without time zone DEFAULT now(),
|
||||
updated_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE public.goadmin_user_permissions (
|
||||
user_id integer NOT NULL,
|
||||
permission_id integer NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now(),
|
||||
updated_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.goadmin_users_myid_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
MAXVALUE 99999999
|
||||
CACHE 1;
|
||||
|
||||
CREATE TABLE public.goadmin_users (
|
||||
id integer DEFAULT nextval('public.goadmin_users_myid_seq'::regclass) NOT NULL,
|
||||
username character varying(100) NOT NULL,
|
||||
password character varying(100) NOT NULL,
|
||||
name character varying(100) NOT NULL,
|
||||
avatar character varying(255),
|
||||
remember_token character varying(100),
|
||||
created_at timestamp without time zone DEFAULT now(),
|
||||
updated_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (1, 0, 1, 1, 'Dashboard', NULL, '', 'fa-bar-chart', '/', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (2, 0, 1, 2, 'Admin', NULL, '', 'fa-tasks', '', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (3, 2, 1, 2, 'Users', NULL, '', 'fa-users', '/info/manager', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (4, 2, 1, 3, 'Roles', NULL, '', 'fa-user', '/info/roles', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (5, 2, 1, 4, 'Permission', NULL, '', 'fa-ban', '/info/permission', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (7, 2, 1, 6, 'Operation log', NULL, '', 'fa-history', '/info/op', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (9, 0, 0, 9, 'Users', '', '', 'fa-users', '/info/users', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (14, 0, 0, 12, 'Github', 'Miscellaneous', '', 'fa-github', 'https://github.com/shtorm-7/sing-box-extended', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (15, 0, 0, 13, 'Donate', '', '', 'fa-heart', 'https://github.com/shtorm-7/sing-box-extended#support-the-project', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (13, 0, 0, 7, 'Squads', 'General', '', 'fa-gg', '/info/squads', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (11, 0, 0, 8, 'Nodes', '', '', 'fa-sitemap', '/info/nodes', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (10, 0, 0, 10, 'Connection limiters', 'Limiters', '', 'fa-plug', '/info/connection_limiters', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_menu (id, parent_id, type, "order", title, header, plugin_name, icon, uri, uuid, created_at, updated_at) VALUES (8, 0, 0, 11, 'Bandwidth limiters', '', '', 'fa-dashboard', '/info/bandwidth_limiters', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
|
||||
|
||||
INSERT INTO public.goadmin_permissions (id, name, slug, http_method, http_path, created_at, updated_at) VALUES (1, 'All permission', '*', '', '*', '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_permissions (id, name, slug, http_method, http_path, created_at, updated_at) VALUES (2, 'Dashboard', 'dashboard', 'GET,PUT,POST,DELETE', '/', '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
|
||||
|
||||
INSERT INTO public.goadmin_role_menu (role_id, menu_id, created_at, updated_at) VALUES (1, 1, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_role_menu (role_id, menu_id, created_at, updated_at) VALUES (1, 7, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_role_menu (role_id, menu_id, created_at, updated_at) VALUES (2, 7, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
|
||||
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (1, 1, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (1, 2, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (2, 2, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
INSERT INTO public.goadmin_role_permissions (role_id, permission_id, created_at, updated_at) VALUES (0, 3, NULL, NULL);
|
||||
|
||||
|
||||
INSERT INTO public.goadmin_role_users (role_id, user_id, created_at, updated_at) VALUES (1, 1, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_role_users (role_id, user_id, created_at, updated_at) VALUES (2, 2, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
|
||||
|
||||
INSERT INTO public.goadmin_roles (id, name, slug, created_at, updated_at) VALUES (1, 'Administrator', 'administrator', '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_roles (id, name, slug, created_at, updated_at) VALUES (2, 'Operator', 'operator', '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
|
||||
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (6, 'site_off', 'false', 0, NULL, 1, '2026-02-15 09:57:02.436501', '2026-02-15 09:57:02.436501');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (7, 'prohibit_config_modification', 'false', 0, NULL, 1, '2026-02-15 09:57:02.441183', '2026-02-15 09:57:02.441183');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (11, 'login_url', '/login', 0, NULL, 1, '2026-02-15 09:57:02.459525', '2026-02-15 09:57:02.459525');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (16, 'open_admin_api', 'false', 0, NULL, 1, '2026-02-15 09:57:02.483908', '2026-02-15 09:57:02.483908');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (18, 'domain', '', 0, NULL, 1, '2026-02-15 09:57:02.493151', '2026-02-15 09:57:02.493151');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (23, 'asset_root_path', './public/', 0, NULL, 1, '2026-02-15 09:57:02.517213', '2026-02-15 09:57:02.517213');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (24, 'url_prefix', 'admin', 0, NULL, 1, '2026-02-15 09:57:02.521815', '2026-02-15 09:57:02.521815');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (33, 'exclude_theme_components', 'null', 0, NULL, 1, '2026-02-15 09:57:02.565725', '2026-02-15 09:57:02.565725');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (39, 'app_id', 'Qn0eh7HQsrt9', 0, NULL, 1, '2026-02-15 09:57:02.592551', '2026-02-15 09:57:02.592551');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (41, 'auth_user_table', 'goadmin_users', 0, NULL, 1, '2026-02-15 09:57:02.601496', '2026-02-15 09:57:02.601496');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (53, 'bootstrap_file_path', '', 0, NULL, 1, '2026-02-15 09:57:02.658984', '2026-02-15 09:57:02.658984');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (55, 'index_url', '/', 0, NULL, 1, '2026-02-15 09:57:02.668457', '2026-02-15 09:57:02.668457');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (66, 'login_logo', '', 0, NULL, 1, '2026-02-15 09:57:02.719608', '2026-02-15 09:57:02.719608');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (67, 'hide_visitor_user_center_entrance', 'false', 0, NULL, 1, '2026-02-15 09:57:02.724307', '2026-02-15 09:57:02.724307');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (68, 'go_mod_file_path', '', 0, NULL, 1, '2026-02-15 09:57:02.728694', '2026-02-15 09:57:02.728694');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (3, 'logger_encoder_caller', 'full', 0, NULL, 1, '2026-02-15 09:57:02.420312', '2026-02-15 09:57:02.420312');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (60, 'logger_encoder_caller_key', 'caller', 0, NULL, 1, '2026-02-15 09:57:02.692189', '2026-02-15 09:57:02.692189');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (34, 'logo', 'Sing-box Extended', 0, NULL, 1, '2026-02-15 09:57:02.570594', '2026-02-15 09:57:02.570594');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (69, 'env', 'prod', 0, NULL, 1, '2026-02-15 09:57:02.733059', '2026-02-15 09:57:02.733059');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (29, 'color_scheme', 'skin-black', 0, NULL, 1, '2026-02-15 09:57:02.545599', '2026-02-15 09:57:02.545599');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (17, 'allow_del_operation_log', 'false', 0, NULL, 1, '2026-02-15 09:57:02.488458', '2026-02-15 09:57:02.488458');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (35, 'info_log_path', '', 0, NULL, 1, '2026-02-15 09:57:02.574649', '2026-02-15 09:57:02.574649');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (22, 'operation_log_off', 'false', 0, NULL, 1, '2026-02-15 09:57:02.512394', '2026-02-15 09:57:02.512394');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (42, 'hide_app_info_entrance', 'false', 0, NULL, 1, '2026-02-15 09:57:02.606071', '2026-02-15 09:57:02.606071');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (12, 'access_log_path', '', 0, NULL, 1, '2026-02-15 09:57:02.464612', '2026-02-15 09:57:02.464612');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (32, 'logger_rotate_max_age', '30', 0, NULL, 1, '2026-02-15 09:57:02.560801', '2026-02-15 09:57:02.560801');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (40, 'custom_foot_html', '', 0, NULL, 1, '2026-02-15 09:57:02.597285', '2026-02-15 09:57:02.597285');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (62, 'logger_encoder_duration', 'string', 0, NULL, 1, '2026-02-15 09:57:02.701522', '2026-02-15 09:57:02.701522');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (65, 'logger_encoder_level_key', 'level', 0, NULL, 1, '2026-02-15 09:57:02.715108', '2026-02-15 09:57:02.715108');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (64, 'debug', 'false', 0, NULL, 1, '2026-02-15 09:57:02.710705', '2026-02-15 09:57:02.710705');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (43, 'hide_plugin_entrance', 'false', 0, NULL, 1, '2026-02-15 09:57:02.610825', '2026-02-15 09:57:02.610825');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (54, 'animation_type', '', 0, NULL, 1, '2026-02-15 09:57:02.663713', '2026-02-15 09:57:02.663713');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (48, 'theme', 'sword', 0, NULL, 1, '2026-02-15 09:57:02.634039', '2026-02-15 09:57:02.634039');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (45, 'info_log_off', 'false', 0, NULL, 1, '2026-02-15 09:57:02.620165', '2026-02-15 09:57:02.620165');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (31, 'error_log_path', '', 0, NULL, 1, '2026-02-15 09:57:02.555798', '2026-02-15 09:57:02.555798');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (5, 'asset_url', '', 0, NULL, 1, '2026-02-15 09:57:02.431855', '2026-02-15 09:57:02.431855');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (36, 'logger_encoder_encoding', 'console', 0, NULL, 1, '2026-02-15 09:57:02.579052', '2026-02-15 09:57:02.579052');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (27, 'login_title', 'Sing-box Extended', 0, NULL, 1, '2026-02-15 09:57:02.536102', '2026-02-15 09:57:02.536102');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (51, 'animation_duration', '0.00', 0, NULL, 1, '2026-02-15 09:57:02.64867', '2026-02-15 09:57:02.64867');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (19, 'file_upload_engine', '{"name":"local"}', 0, NULL, 1, '2026-02-15 09:57:02.49794', '2026-02-15 09:57:02.49794');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (26, 'logger_encoder_time', 'iso8601', 0, NULL, 1, '2026-02-15 09:57:02.531365', '2026-02-15 09:57:02.531365');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (10, 'custom_404_html', '', 0, NULL, 1, '2026-02-15 09:57:02.454777', '2026-02-15 09:57:02.454777');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (58, 'sql_log', 'false', 0, NULL, 1, '2026-02-15 09:57:02.682567', '2026-02-15 09:57:02.682567');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (2, 'logger_encoder_message_key', 'msg', 0, NULL, 1, '2026-02-15 09:57:02.415189', '2026-02-15 09:57:02.415189');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (46, 'logger_encoder_stacktrace_key', 'stacktrace', 0, NULL, 1, '2026-02-15 09:57:02.624977', '2026-02-15 09:57:02.624977');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (63, 'mini_logo', 'SBE', 0, NULL, 1, '2026-02-15 09:57:02.706145', '2026-02-15 09:57:02.706145');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (38, 'custom_403_html', '', 0, NULL, 1, '2026-02-15 09:57:02.588062', '2026-02-15 09:57:02.588062');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (30, 'language', 'en', 0, NULL, 1, '2026-02-15 09:57:02.550466', '2026-02-15 09:57:02.550466');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (15, 'hide_config_center_entrance', 'false', 0, NULL, 1, '2026-02-15 09:57:02.479097', '2026-02-15 09:57:02.479097');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (59, 'logger_rotate_max_backups', '5', 0, NULL, 1, '2026-02-15 09:57:02.687429', '2026-02-15 09:57:02.687429');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (57, 'custom_head_html', '', 0, NULL, 1, '2026-02-15 09:57:02.677723', '2026-02-15 09:57:02.677723');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (52, 'custom_500_html', '', 0, NULL, 1, '2026-02-15 09:57:02.654236', '2026-02-15 09:57:02.654236');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (44, 'title', 'Sing-box Extended', 0, NULL, 1, '2026-02-15 09:57:02.615471', '2026-02-15 09:57:02.615471');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (47, 'session_life_time', '7200', 0, NULL, 1, '2026-02-15 09:57:02.629619', '2026-02-15 09:57:02.629619');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (8, 'access_log_off', 'false', 0, NULL, 1, '2026-02-15 09:57:02.445593', '2026-02-15 09:57:02.445593');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (49, 'error_log_off', 'false', 0, NULL, 1, '2026-02-15 09:57:02.6385', '2026-02-15 09:57:02.6385');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (50, 'logger_rotate_max_size', '10', 0, NULL, 1, '2026-02-15 09:57:02.643733', '2026-02-15 09:57:02.643733');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (14, 'logger_rotate_compress', 'false', 0, NULL, 1, '2026-02-15 09:57:02.474296', '2026-02-15 09:57:02.474296');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (13, 'logger_encoder_time_key', 'ts', 0, NULL, 1, '2026-02-15 09:57:02.469396', '2026-02-15 09:57:02.469396');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (37, 'animation_delay', '0.00', 0, NULL, 1, '2026-02-15 09:57:02.583815', '2026-02-15 09:57:02.583815');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (20, 'extra', '', 0, NULL, 1, '2026-02-15 09:57:02.50276', '2026-02-15 09:57:02.50276');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (25, 'access_assets_log_off', 'false', 0, NULL, 1, '2026-02-15 09:57:02.526618', '2026-02-15 09:57:02.526618');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (4, 'logger_level', '0', 0, NULL, 1, '2026-02-15 09:57:02.426736', '2026-02-15 09:57:02.426736');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (9, 'footer_info', '', 0, NULL, 1, '2026-02-15 09:57:02.450409', '2026-02-15 09:57:02.450409');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (21, 'no_limit_login_ip', 'false', 0, NULL, 1, '2026-02-15 09:57:02.507609', '2026-02-15 09:57:02.507609');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (28, 'hide_tool_entrance', 'false', 0, NULL, 1, '2026-02-15 09:57:02.540813', '2026-02-15 09:57:02.540813');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (61, 'logger_encoder_level', 'capitalColor', 0, NULL, 1, '2026-02-15 09:57:02.696859', '2026-02-15 09:57:02.696859');
|
||||
INSERT INTO public.goadmin_site (id, key, value, type, description, state, created_at, updated_at) VALUES (56, 'logger_encoder_name_key', 'logger', 0, NULL, 1, '2026-02-15 09:57:02.672962', '2026-02-15 09:57:02.672962');
|
||||
|
||||
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (1, 1, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (2, 2, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
INSERT INTO public.goadmin_user_permissions (user_id, permission_id, created_at, updated_at) VALUES (0, 1, NULL, NULL);
|
||||
|
||||
|
||||
INSERT INTO public.goadmin_users (id, username, password, name, avatar, remember_token, created_at, updated_at) VALUES (2, 'operator', '$2a$10$rVqkOzHjN2MdlEprRflb1eGP0oZXuSrbJLOmJagFsCd81YZm0bsh.', 'Operator', '', NULL, '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
INSERT INTO public.goadmin_users (id, username, password, name, avatar, remember_token, created_at, updated_at) VALUES (1, 'admin', '$2a$10$ilNHHnX5S6EMw.Ffc1Y1JezYCyquFIO.7Z0vLr1eHJUXnGy4cdrtq', 'admin', '', 'tlNcBVK9AvfYH7WEnwB1RKvocJu8FfRy4um3DJtwdHuJy0dwFsLOgAc0xUfh', '2019-09-10 00:00:00', '2019-09-10 00:00:00');
|
||||
|
||||
|
||||
SELECT pg_catalog.setval('public.goadmin_menu_myid_seq', 12, true);
|
||||
|
||||
|
||||
SELECT pg_catalog.setval('public.goadmin_operation_log_myid_seq', 11, true);
|
||||
|
||||
|
||||
SELECT pg_catalog.setval('public.goadmin_permissions_myid_seq', 2, true);
|
||||
|
||||
|
||||
SELECT pg_catalog.setval('public.goadmin_roles_myid_seq', 2, true);
|
||||
|
||||
|
||||
SELECT pg_catalog.setval('public.goadmin_session_myid_seq', 7, true);
|
||||
|
||||
|
||||
SELECT pg_catalog.setval('public.goadmin_site_myid_seq', 69, true);
|
||||
|
||||
|
||||
SELECT pg_catalog.setval('public.goadmin_users_myid_seq', 2, true);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.goadmin_menu
|
||||
ADD CONSTRAINT goadmin_menu_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.goadmin_operation_log
|
||||
ADD CONSTRAINT goadmin_operation_log_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.goadmin_permissions
|
||||
ADD CONSTRAINT goadmin_permissions_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.goadmin_roles
|
||||
ADD CONSTRAINT goadmin_roles_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.goadmin_session
|
||||
ADD CONSTRAINT goadmin_session_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.goadmin_site
|
||||
ADD CONSTRAINT goadmin_site_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
ALTER TABLE ONLY public.goadmin_users
|
||||
ADD CONSTRAINT goadmin_users_pkey PRIMARY KEY (id);
|
||||
`,
|
||||
"1_initialize_schema.down.sql": ``,
|
||||
}
|
||||
|
||||
func MigratePostgreSQL(db *sql.DB) error {
|
||||
driver, err := postgres.WithInstance(db, &postgres.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sourceDriver := source.NewRawDriver(migrations)
|
||||
if err := sourceDriver.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := migrate.NewWithInstance(
|
||||
"raw",
|
||||
sourceDriver,
|
||||
"postgres",
|
||||
driver,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.Up()
|
||||
}
|
||||
13
service/admin_panel/pages/dashboard.go
Normal file
13
service/admin_panel/pages/dashboard.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"github.com/GoAdminGroup/go-admin/context"
|
||||
"github.com/GoAdminGroup/go-admin/template/types"
|
||||
)
|
||||
|
||||
func DashboardPage(ctx *context.Context) (types.Panel, error) {
|
||||
|
||||
return types.Panel{
|
||||
Title: "Dashboard",
|
||||
}, nil
|
||||
}
|
||||
188
service/admin_panel/service.go
Normal file
188
service/admin_panel/service.go
Normal file
@@ -0,0 +1,188 @@
|
||||
//go:build with_admin_panel
|
||||
|
||||
package admin_panel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
_ "github.com/lib/pq"
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
_ "github.com/GoAdminGroup/go-admin/adapter/chi"
|
||||
"github.com/GoAdminGroup/go-admin/engine"
|
||||
"github.com/GoAdminGroup/go-admin/modules/config"
|
||||
_ "github.com/GoAdminGroup/go-admin/modules/db/drivers/sqlite"
|
||||
"github.com/GoAdminGroup/go-admin/plugins/admin/modules/table"
|
||||
"github.com/GoAdminGroup/go-admin/template"
|
||||
"github.com/GoAdminGroup/go-admin/template/chartjs"
|
||||
_ "github.com/GoAdminGroup/themes/adminlte"
|
||||
_ "github.com/GoAdminGroup/themes/sword"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
boxService "github.com/sagernet/sing-box/adapter/service"
|
||||
"github.com/sagernet/sing-box/common/listener"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/service/admin_panel/migration"
|
||||
"github.com/sagernet/sing-box/service/admin_panel/pages"
|
||||
"github.com/sagernet/sing-box/service/admin_panel/tables"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
aTLS "github.com/sagernet/sing/common/tls"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
func RegisterService(registry *boxService.Registry) {
|
||||
boxService.Register[option.AdminPanelServiceOptions](registry, C.TypeAdminPanel, NewService)
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
boxService.Adapter
|
||||
ctx context.Context
|
||||
logger log.ContextLogger
|
||||
listener *listener.Listener
|
||||
tlsConfig tls.ServerConfig
|
||||
httpServer *http.Server
|
||||
options option.AdminPanelServiceOptions
|
||||
}
|
||||
|
||||
func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.AdminPanelServiceOptions) (adapter.Service, error) {
|
||||
s := &Service{
|
||||
Adapter: boxService.NewAdapter(C.TypeAdminPanel, tag),
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
listener: listener.New(listener.Options{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Network: []string{N.NetworkTCP},
|
||||
Listen: options.ListenOptions,
|
||||
}),
|
||||
options: options,
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *Service) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
boxManager := service.FromContext[adapter.ServiceManager](s.ctx)
|
||||
service, ok := boxManager.Get(s.options.Manager)
|
||||
if !ok {
|
||||
return E.New("manager ", s.options.Manager, " not found")
|
||||
}
|
||||
manager, ok := service.(CM.Manager)
|
||||
if !ok {
|
||||
return E.New("invalid ", s.options.Manager, " manager")
|
||||
}
|
||||
switch s.options.Database.Driver {
|
||||
case "postgresql":
|
||||
db, err := sql.Open("postgres", s.options.Database.DSN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
if err := migration.MigratePostgreSQL(db); err != nil && err != migrate.ErrNoChange {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return E.New("unknown driver \"", s.options.Database.Driver, "\"")
|
||||
}
|
||||
var generators = map[string]table.Generator{
|
||||
"squads": tables.SquadTableFactory(
|
||||
manager,
|
||||
s.logger,
|
||||
),
|
||||
"nodes": tables.NodeTableFactory(
|
||||
manager,
|
||||
s.logger,
|
||||
),
|
||||
"users": tables.UserTableFactory(
|
||||
manager,
|
||||
s.logger,
|
||||
),
|
||||
"connection_limiters": tables.ConnectionLimiterTableFactory(
|
||||
manager,
|
||||
s.logger,
|
||||
),
|
||||
"bandwidth_limiters": tables.BandwidthLimiterTableFactory(
|
||||
manager,
|
||||
s.logger,
|
||||
),
|
||||
}
|
||||
eng := engine.Default()
|
||||
chiRouter := chi.NewRouter()
|
||||
template.AddComp(chartjs.NewChart())
|
||||
if err := eng.AddConfig(&config.Config{
|
||||
UrlPrefix: "admin",
|
||||
IndexUrl: "/",
|
||||
LoginUrl: "/login",
|
||||
Databases: config.DatabaseList{
|
||||
"default": config.Database{
|
||||
Driver: s.options.Database.Driver,
|
||||
Dsn: s.options.Database.DSN,
|
||||
},
|
||||
},
|
||||
}).
|
||||
AddGenerators(generators).
|
||||
Use(chiRouter); err != nil {
|
||||
return err
|
||||
}
|
||||
eng.HTML("GET", "/admin", pages.DashboardPage)
|
||||
chiRouter.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/admin", http.StatusMovedPermanently)
|
||||
})
|
||||
chiRouter.Get("/admin/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/admin", http.StatusMovedPermanently)
|
||||
})
|
||||
if s.options.TLS != nil {
|
||||
tlsConfig, err := tls.NewServer(s.ctx, s.logger, common.PtrValueOrDefault(s.options.TLS))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.tlsConfig = tlsConfig
|
||||
}
|
||||
if s.tlsConfig != nil {
|
||||
err := s.tlsConfig.Start()
|
||||
if err != nil {
|
||||
return E.Cause(err, "create TLS config")
|
||||
}
|
||||
}
|
||||
tcpListener, err := s.listener.ListenTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.tlsConfig != nil {
|
||||
if !common.Contains(s.tlsConfig.NextProtos(), http2.NextProtoTLS) {
|
||||
s.tlsConfig.SetNextProtos(append([]string{"h2"}, s.tlsConfig.NextProtos()...))
|
||||
}
|
||||
tcpListener = aTLS.NewListener(tcpListener, s.tlsConfig)
|
||||
}
|
||||
s.httpServer = &http.Server{
|
||||
Handler: chiRouter,
|
||||
}
|
||||
go func() {
|
||||
err = s.httpServer.Serve(tcpListener)
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
s.logger.Error("serve error: ", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) Close() error {
|
||||
return common.Close(
|
||||
common.PtrOrNil(s.httpServer),
|
||||
common.PtrOrNil(s.listener),
|
||||
s.tlsConfig,
|
||||
)
|
||||
}
|
||||
20
service/admin_panel/service_stub.go
Normal file
20
service/admin_panel/service_stub.go
Normal file
@@ -0,0 +1,20 @@
|
||||
//go:build !with_admin_panel
|
||||
|
||||
package admin_panel
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/adapter/service"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func RegisterService(registry *service.Registry) {
|
||||
service.Register[option.AdminPanelServiceOptions](registry, C.TypeAdminPanel, func(ctx context.Context, logger log.ContextLogger, tag string, options option.AdminPanelServiceOptions) (adapter.Service, error) {
|
||||
return nil, E.New(`Admin panel is not included in this build, rebuild with -tags with_admin_panel`)
|
||||
})
|
||||
}
|
||||
259
service/admin_panel/tables/bandwidth_limiter.go
Normal file
259
service/admin_panel/tables/bandwidth_limiter.go
Normal file
@@ -0,0 +1,259 @@
|
||||
package tables
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/GoAdminGroup/go-admin/context"
|
||||
"github.com/GoAdminGroup/go-admin/modules/db"
|
||||
mForm "github.com/GoAdminGroup/go-admin/plugins/admin/modules/form"
|
||||
"github.com/GoAdminGroup/go-admin/plugins/admin/modules/parameter"
|
||||
"github.com/GoAdminGroup/go-admin/plugins/admin/modules/table"
|
||||
"github.com/GoAdminGroup/go-admin/template"
|
||||
"github.com/GoAdminGroup/go-admin/template/types"
|
||||
"github.com/GoAdminGroup/go-admin/template/types/form"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
)
|
||||
|
||||
func BandwidthLimiterTableFactory(manager CM.Manager, logger log.Logger) func(ctx *context.Context) table.Table {
|
||||
return func(ctx *context.Context) table.Table {
|
||||
t := table.NewDefaultTable(ctx, table.Config{
|
||||
CanAdd: true,
|
||||
Editable: true,
|
||||
Deletable: true,
|
||||
Exportable: true,
|
||||
PrimaryKey: table.PrimaryKey{
|
||||
Type: db.Int,
|
||||
Name: table.DefaultPrimaryKeyName,
|
||||
},
|
||||
})
|
||||
squads, err := manager.GetSquads(map[string][]string{})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
squadsByID := make(map[int]string, len(squads))
|
||||
squadOptions := make(types.FieldOptions, len(squads))
|
||||
for i, squad := range squads {
|
||||
squadsByID[squad.ID] = squad.Name
|
||||
squadOptions[i] = types.FieldOption{
|
||||
Text: squad.Name,
|
||||
Value: strconv.Itoa(squad.ID),
|
||||
}
|
||||
}
|
||||
info := t.GetInfo().SetFilterFormLayout(form.LayoutFilter)
|
||||
info.AddField("ID", "id", db.Int).
|
||||
FieldSortable()
|
||||
info.AddField("Squads", "squad_ids", db.Varchar).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
values := model.Row["squad_ids"].([]interface{})
|
||||
labels := template.HTML("")
|
||||
labelTpl := label(ctx).SetType("success")
|
||||
labelValues := make([]string, len(values))
|
||||
for i, squadID := range values {
|
||||
labelValues[i] = squadsByID[int(squadID.(float64))]
|
||||
}
|
||||
for key, label := range labelValues {
|
||||
if key == len(labelValues)-1 {
|
||||
labels += labelTpl.SetContent(template.HTML(label)).GetContent()
|
||||
} else {
|
||||
labels += labelTpl.SetContent(template.HTML(label)).GetContent()
|
||||
}
|
||||
}
|
||||
return labels
|
||||
})
|
||||
info.AddField("Username", "username", db.Varchar).
|
||||
FieldFilterable().
|
||||
FieldSortable()
|
||||
info.AddField("Outbound", "outbound", db.Varchar).
|
||||
FieldFilterable().
|
||||
FieldSortable()
|
||||
info.AddField("Strategy", "strategy", db.Varchar).
|
||||
FieldFilterable(types.FilterType{
|
||||
FormType: form.SelectSingle,
|
||||
Options: types.FieldOptions{
|
||||
{Text: "Connection", Value: "connection"},
|
||||
{Text: "Global", Value: "global"},
|
||||
},
|
||||
}).
|
||||
FieldSortable()
|
||||
info.AddField("Mode", "mode", db.Varchar).
|
||||
FieldFilterable(types.FilterType{
|
||||
FormType: form.SelectSingle,
|
||||
Options: types.FieldOptions{
|
||||
{Text: "Download", Value: "download"},
|
||||
{Text: "Upload", Value: "upload"},
|
||||
{Text: "Duplex", Value: "duplex"},
|
||||
},
|
||||
}).
|
||||
FieldSortable()
|
||||
info.AddField("Connection type", "connection_type", db.Varchar).
|
||||
FieldFilterable(types.FilterType{
|
||||
FormType: form.SelectSingle,
|
||||
Options: types.FieldOptions{
|
||||
{Text: "HWID", Value: "hwid"},
|
||||
{Text: "Mux", Value: "mux"},
|
||||
{Text: "IP", Value: "ip"},
|
||||
},
|
||||
}).
|
||||
FieldSortable()
|
||||
info.AddField("Speed", "speed", db.Varchar).
|
||||
FieldSortable()
|
||||
info.AddField("Created at", "created_at", db.Datetime).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
t, err := time.Parse(time.RFC3339, model.Value)
|
||||
if err != nil {
|
||||
return model.Value
|
||||
}
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}).
|
||||
FieldFilterable(types.FilterType{FormType: form.DatetimeRange}).
|
||||
FieldSortable()
|
||||
info.AddField("Updated at", "updated_at", db.Datetime).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
t, err := time.Parse(time.RFC3339, model.Value)
|
||||
if err != nil {
|
||||
return model.Value
|
||||
}
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}).
|
||||
FieldFilterable(types.FilterType{FormType: form.DatetimeRange}).
|
||||
FieldSortable()
|
||||
|
||||
info.SetGetDataFn(func(param parameter.Parameters) ([]map[string]interface{}, int) {
|
||||
filters := make(map[string][]string)
|
||||
listFilters := map[string][]string{
|
||||
"offset": {strconv.Itoa((param.PageInt - 1) * param.PageSizeInt)},
|
||||
"limit": {param.PageSize},
|
||||
}
|
||||
for k, v := range param.Fields {
|
||||
if strings.HasPrefix(k, "__") {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSuffix(k, "__goadmin")
|
||||
filters[key] = v
|
||||
listFilters[key] = v
|
||||
}
|
||||
if param.SortField != "" {
|
||||
if param.SortType == "asc" {
|
||||
listFilters["sort_asc"] = []string{param.SortField}
|
||||
} else {
|
||||
listFilters["sort_desc"] = []string{param.SortField}
|
||||
}
|
||||
}
|
||||
items, err := manager.GetBandwidthLimiters(listFilters)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, 0
|
||||
}
|
||||
count, err := manager.GetBandwidthLimitersCount(filters)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, 0
|
||||
}
|
||||
result := make([]map[string]interface{}, 0, len(items))
|
||||
for _, item := range items {
|
||||
var data map[string]interface{}
|
||||
raw, _ := json.Marshal(item)
|
||||
json.Unmarshal(raw, &data)
|
||||
result = append(result, data)
|
||||
}
|
||||
return result, count
|
||||
})
|
||||
|
||||
info.SetDeleteFn(func(ids []string) error {
|
||||
for _, id := range ids {
|
||||
i, err := strconv.Atoi(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := manager.DeleteBandwidthLimiter(i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
info.SetTable("bandwidth_limiters").SetTitle("Bandwidth Limiters").SetDescription("Bandwidth Limiters")
|
||||
|
||||
formList := t.GetForm()
|
||||
formList.AddField("ID", "id", db.Int, form.Default).
|
||||
FieldNotAllowAdd().
|
||||
FieldNotAllowEdit()
|
||||
formList.AddField("Squads", "squad_ids", db.Varchar, form.Select).
|
||||
FieldMust().
|
||||
FieldOptions(squadOptions).
|
||||
FieldDisableWhenUpdate()
|
||||
formList.AddField("Username", "username", db.Varchar, form.Text).
|
||||
FieldMust().
|
||||
FieldDisplayButCanNotEditWhenUpdate()
|
||||
formList.AddField("Outbound", "outbound", db.Varchar, form.Text).
|
||||
FieldMust().
|
||||
FieldDisplayButCanNotEditWhenUpdate()
|
||||
formList.AddField("Strategy", "strategy", db.Varchar, form.SelectSingle).
|
||||
FieldMust().
|
||||
FieldOptions(types.FieldOptions{
|
||||
{Text: "Connection", Value: "connection"},
|
||||
{Text: "Global", Value: "global"},
|
||||
}).
|
||||
FieldOnChooseOptionsHide([]string{"", "global"}, "connection_type")
|
||||
formList.AddField("Mode", "mode", db.Varchar, form.SelectSingle).
|
||||
FieldMust().
|
||||
FieldOptions(types.FieldOptions{
|
||||
{Text: "Download", Value: "download"},
|
||||
{Text: "Upload", Value: "upload"},
|
||||
{Text: "Duplex", Value: "duplex"},
|
||||
})
|
||||
formList.AddField("Connection type", "connection_type", db.Varchar, form.SelectSingle).
|
||||
FieldOptions(types.FieldOptions{
|
||||
{Text: "HWID", Value: "hwid"},
|
||||
{Text: "Mux", Value: "mux"},
|
||||
{Text: "IP", Value: "ip"},
|
||||
})
|
||||
formList.AddField("Speed", "speed", db.Varchar, form.Text).
|
||||
FieldMust()
|
||||
|
||||
formList.SetInsertFn(func(values mForm.Values) error {
|
||||
squadIDs := make([]int, len(values["squad_ids[]"]))
|
||||
for i, rawSquadID := range values["squad_ids[]"] {
|
||||
squadID, err := strconv.Atoi(rawSquadID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
squadIDs[i] = squadID
|
||||
}
|
||||
_, err := manager.CreateBandwidthLimiter(CM.BandwidthLimiterCreate{
|
||||
SquadIDs: squadIDs,
|
||||
Username: values.Get("username"),
|
||||
Outbound: values.Get("outbound"),
|
||||
Strategy: values.Get("strategy"),
|
||||
Mode: values.Get("mode"),
|
||||
ConnectionType: values.Get("connection_type"),
|
||||
Speed: values.Get("speed"),
|
||||
})
|
||||
return err
|
||||
})
|
||||
|
||||
formList.SetUpdateFn(func(values mForm.Values) error {
|
||||
id, err := strconv.Atoi(values.Get("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = manager.UpdateBandwidthLimiter(id, CM.BandwidthLimiterUpdate{
|
||||
Username: values.Get("username"),
|
||||
Outbound: values.Get("outbound"),
|
||||
Strategy: values.Get("strategy"),
|
||||
Mode: values.Get("mode"),
|
||||
ConnectionType: values.Get("connection_type"),
|
||||
Speed: values.Get("speed"),
|
||||
})
|
||||
return err
|
||||
})
|
||||
|
||||
formList.SetTable("bandwidth_limiters").SetTitle("Bandwidth Limiters").SetDescription("Bandwidth Limiters")
|
||||
return t
|
||||
}
|
||||
}
|
||||
261
service/admin_panel/tables/connection_limiter.go
Normal file
261
service/admin_panel/tables/connection_limiter.go
Normal file
@@ -0,0 +1,261 @@
|
||||
package tables
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/GoAdminGroup/go-admin/context"
|
||||
"github.com/GoAdminGroup/go-admin/modules/db"
|
||||
mForm "github.com/GoAdminGroup/go-admin/plugins/admin/modules/form"
|
||||
"github.com/GoAdminGroup/go-admin/plugins/admin/modules/parameter"
|
||||
"github.com/GoAdminGroup/go-admin/plugins/admin/modules/table"
|
||||
"github.com/GoAdminGroup/go-admin/template"
|
||||
"github.com/GoAdminGroup/go-admin/template/types"
|
||||
"github.com/GoAdminGroup/go-admin/template/types/form"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
)
|
||||
|
||||
func ConnectionLimiterTableFactory(manager CM.Manager, logger log.Logger) func(ctx *context.Context) table.Table {
|
||||
return func(ctx *context.Context) table.Table {
|
||||
connectionLimiterTable := table.NewDefaultTable(ctx, table.Config{
|
||||
CanAdd: true,
|
||||
Editable: true,
|
||||
Deletable: true,
|
||||
Exportable: true,
|
||||
PrimaryKey: table.PrimaryKey{
|
||||
Type: db.Int,
|
||||
Name: table.DefaultPrimaryKeyName,
|
||||
},
|
||||
})
|
||||
squads, err := manager.GetSquads(map[string][]string{})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
squadsByID := make(map[int]string, len(squads))
|
||||
squadOptions := make(types.FieldOptions, len(squads))
|
||||
for i, squad := range squads {
|
||||
squadsByID[squad.ID] = squad.Name
|
||||
squadOptions[i] = types.FieldOption{
|
||||
Text: squad.Name,
|
||||
Value: strconv.Itoa(squad.ID),
|
||||
}
|
||||
}
|
||||
info := connectionLimiterTable.GetInfo().SetFilterFormLayout(form.LayoutFilter)
|
||||
info.AddField("ID", "id", db.Int).
|
||||
FieldSortable()
|
||||
info.AddField("Squads", "squad_ids", db.Varchar).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
values := model.Row["squad_ids"].([]interface{})
|
||||
labels := template.HTML("")
|
||||
labelTpl := label(ctx).SetType("success")
|
||||
labelValues := make([]string, len(values))
|
||||
for i, squadID := range values {
|
||||
labelValues[i] = squadsByID[int(squadID.(float64))]
|
||||
}
|
||||
for key, label := range labelValues {
|
||||
if key == len(labelValues)-1 {
|
||||
labels += labelTpl.SetContent(template.HTML(label)).GetContent()
|
||||
} else {
|
||||
labels += labelTpl.SetContent(template.HTML(label)).GetContent()
|
||||
}
|
||||
}
|
||||
return labels
|
||||
})
|
||||
info.AddField("Username", "username", db.Varchar).
|
||||
FieldFilterable().
|
||||
FieldSortable()
|
||||
info.AddField("Outbound", "outbound", db.Varchar).
|
||||
FieldFilterable().
|
||||
FieldSortable()
|
||||
info.AddField("Strategy", "strategy", db.Varchar).
|
||||
FieldFilterable(types.FilterType{
|
||||
FormType: form.SelectSingle,
|
||||
Options: types.FieldOptions{
|
||||
{Text: "Connection", Value: "connection"},
|
||||
},
|
||||
}).
|
||||
FieldSortable()
|
||||
info.AddField("Connection type", "connection_type", db.Varchar).
|
||||
FieldFilterable(types.FilterType{
|
||||
FormType: form.SelectSingle,
|
||||
Options: types.FieldOptions{
|
||||
{Text: "Mux", Value: "mux"},
|
||||
{Text: "HWID", Value: "hwid"},
|
||||
{Text: "IP", Value: "ip"},
|
||||
},
|
||||
}).
|
||||
FieldSortable()
|
||||
info.AddField("Lock type", "lock_type", db.Varchar).
|
||||
FieldFilterable(types.FilterType{
|
||||
FormType: form.SelectSingle,
|
||||
Options: types.FieldOptions{
|
||||
{Text: "Manager", Value: "manager"},
|
||||
},
|
||||
}).
|
||||
FieldSortable()
|
||||
info.AddField("Count", "count", db.Int).
|
||||
FieldSortable()
|
||||
info.AddField("Created at", "created_at", db.Datetime).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
t, err := time.Parse(time.RFC3339, model.Value)
|
||||
if err != nil {
|
||||
return model.Value
|
||||
}
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}).
|
||||
FieldFilterable(types.FilterType{FormType: form.DatetimeRange}).
|
||||
FieldSortable()
|
||||
info.AddField("Updated at", "updated_at", db.Datetime).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
t, err := time.Parse(time.RFC3339, model.Value)
|
||||
if err != nil {
|
||||
return model.Value
|
||||
}
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}).
|
||||
FieldFilterable(types.FilterType{FormType: form.DatetimeRange}).
|
||||
FieldSortable()
|
||||
|
||||
info.SetGetDataFn(func(param parameter.Parameters) ([]map[string]interface{}, int) {
|
||||
filters := make(map[string][]string)
|
||||
listFilters := map[string][]string{
|
||||
"offset": {strconv.Itoa((param.PageInt - 1) * param.PageSizeInt)},
|
||||
"limit": {param.PageSize},
|
||||
}
|
||||
for k, v := range param.Fields {
|
||||
if strings.HasPrefix(k, "__") {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSuffix(k, "__goadmin")
|
||||
filters[key] = v
|
||||
listFilters[key] = v
|
||||
}
|
||||
if param.SortField != "" {
|
||||
if param.SortType == "asc" {
|
||||
listFilters["sort_asc"] = []string{param.SortField}
|
||||
} else {
|
||||
listFilters["sort_desc"] = []string{param.SortField}
|
||||
}
|
||||
}
|
||||
items, err := manager.GetConnectionLimiters(listFilters)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, 0
|
||||
}
|
||||
count, err := manager.GetConnectionLimitersCount(filters)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, 0
|
||||
}
|
||||
result := make([]map[string]interface{}, 0, len(items))
|
||||
for _, item := range items {
|
||||
var data map[string]interface{}
|
||||
raw, _ := json.Marshal(item)
|
||||
json.Unmarshal(raw, &data)
|
||||
result = append(result, data)
|
||||
}
|
||||
return result, count
|
||||
})
|
||||
|
||||
info.SetDeleteFn(func(ids []string) error {
|
||||
for _, id := range ids {
|
||||
i, err := strconv.Atoi(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := manager.DeleteConnectionLimiter(i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
info.SetTable("connection_limiters").SetTitle("Connection Limiters").SetDescription("Connection Limiters")
|
||||
|
||||
formList := connectionLimiterTable.GetForm()
|
||||
formList.AddField("ID", "id", db.Int, form.Default).
|
||||
FieldNotAllowAdd().
|
||||
FieldNotAllowEdit()
|
||||
formList.AddField("Squads", "squad_ids", db.Varchar, form.Select).
|
||||
FieldMust().
|
||||
FieldOptions(squadOptions).
|
||||
FieldDisableWhenUpdate()
|
||||
formList.AddField("Username", "username", db.Varchar, form.Text).
|
||||
FieldMust().
|
||||
FieldDisplayButCanNotEditWhenUpdate()
|
||||
formList.AddField("Outbound", "outbound", db.Varchar, form.Text).
|
||||
FieldMust().
|
||||
FieldDisplayButCanNotEditWhenUpdate()
|
||||
formList.AddField("Strategy", "strategy", db.Varchar, form.SelectSingle).
|
||||
FieldMust().
|
||||
FieldOptions(types.FieldOptions{
|
||||
{Text: "Connection", Value: "connection"},
|
||||
}).
|
||||
FieldDefault("connection")
|
||||
formList.AddField("Connection type", "connection_type", db.Varchar, form.SelectSingle).
|
||||
FieldOptions(types.FieldOptions{
|
||||
{Text: "Mux", Value: "mux"},
|
||||
{Text: "HWID", Value: "hwid"},
|
||||
{Text: "IP", Value: "ip"},
|
||||
})
|
||||
formList.AddField("Lock type", "lock_type", db.Varchar, form.SelectSingle).
|
||||
FieldOptions(types.FieldOptions{
|
||||
{Text: "Manager", Value: "manager"},
|
||||
})
|
||||
formList.AddField("Count", "count", db.Int, form.Number).
|
||||
FieldMust().
|
||||
FieldDefault("0")
|
||||
|
||||
formList.SetInsertFn(func(values mForm.Values) error {
|
||||
squadIDs := make([]int, len(values["squad_ids[]"]))
|
||||
for i, rawSquadID := range values["squad_ids[]"] {
|
||||
squadID, err := strconv.Atoi(rawSquadID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
squadIDs[i] = squadID
|
||||
}
|
||||
count, err := strconv.ParseUint(values.Get("count"), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = manager.CreateConnectionLimiter(CM.ConnectionLimiterCreate{
|
||||
SquadIDs: squadIDs,
|
||||
Username: values.Get("username"),
|
||||
Outbound: values.Get("outbound"),
|
||||
Strategy: values.Get("strategy"),
|
||||
ConnectionType: values.Get("connection_type"),
|
||||
LockType: values.Get("lock_type"),
|
||||
Count: uint32(count),
|
||||
})
|
||||
return err
|
||||
})
|
||||
|
||||
formList.SetUpdateFn(func(values mForm.Values) error {
|
||||
id, err := strconv.Atoi(values.Get("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count, err := strconv.ParseUint(values.Get("count"), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = manager.UpdateConnectionLimiter(id, CM.ConnectionLimiterUpdate{
|
||||
Username: values.Get("username"),
|
||||
Outbound: values.Get("outbound"),
|
||||
Strategy: values.Get("strategy"),
|
||||
ConnectionType: values.Get("connection_type"),
|
||||
LockType: values.Get("lock_type"),
|
||||
Count: uint32(count),
|
||||
})
|
||||
return err
|
||||
})
|
||||
|
||||
formList.SetTable("connection_limiters").SetTitle("Connection Limiters").SetDescription("Connection Limiters")
|
||||
return connectionLimiterTable
|
||||
}
|
||||
}
|
||||
201
service/admin_panel/tables/node.go
Normal file
201
service/admin_panel/tables/node.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package tables
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/GoAdminGroup/go-admin/context"
|
||||
"github.com/GoAdminGroup/go-admin/modules/config"
|
||||
"github.com/GoAdminGroup/go-admin/modules/db"
|
||||
mForm "github.com/GoAdminGroup/go-admin/plugins/admin/modules/form"
|
||||
"github.com/GoAdminGroup/go-admin/plugins/admin/modules/parameter"
|
||||
"github.com/GoAdminGroup/go-admin/plugins/admin/modules/table"
|
||||
"github.com/GoAdminGroup/go-admin/template"
|
||||
"github.com/GoAdminGroup/go-admin/template/types"
|
||||
"github.com/GoAdminGroup/go-admin/template/types/form"
|
||||
"github.com/gofrs/uuid/v5"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
)
|
||||
|
||||
func label(ctx *context.Context) types.LabelAttribute {
|
||||
return template.Get(ctx, config.GetTheme()).Label().SetType("success")
|
||||
}
|
||||
|
||||
func NodeTableFactory(manager CM.Manager, logger log.Logger) func(ctx *context.Context) (nodeTable table.Table) {
|
||||
return func(ctx *context.Context) (nodeTable table.Table) {
|
||||
nodeTable = table.NewDefaultTable(ctx, table.Config{
|
||||
CanAdd: true,
|
||||
Editable: true,
|
||||
Deletable: true,
|
||||
Exportable: true,
|
||||
PrimaryKey: table.PrimaryKey{
|
||||
Type: db.Varchar,
|
||||
Name: "uuid",
|
||||
},
|
||||
})
|
||||
squads, err := manager.GetSquads(map[string][]string{})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
squadsByID := make(map[int]string, len(squads))
|
||||
squadOptions := make(types.FieldOptions, len(squads))
|
||||
for i, squad := range squads {
|
||||
squadsByID[squad.ID] = squad.Name
|
||||
squadOptions[i] = types.FieldOption{
|
||||
Text: squad.Name,
|
||||
Value: strconv.Itoa(squad.ID),
|
||||
}
|
||||
}
|
||||
info := nodeTable.GetInfo().SetFilterFormLayout(form.LayoutFilter)
|
||||
info.AddField("UUID", "uuid", db.Varchar).
|
||||
FieldFilterable().
|
||||
FieldSortable()
|
||||
info.AddField("Name", "name", db.Varchar).
|
||||
FieldFilterable().
|
||||
FieldSortable()
|
||||
info.AddField("Squads", "squad_ids", db.Varchar).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
values := model.Row["squad_ids"].([]interface{})
|
||||
labels := template.HTML("")
|
||||
labelTpl := label(ctx).SetType("success")
|
||||
labelValues := make([]string, len(values))
|
||||
for i, squadID := range values {
|
||||
labelValues[i] = squadsByID[int(squadID.(float64))]
|
||||
}
|
||||
for key, label := range labelValues {
|
||||
if key == len(labelValues)-1 {
|
||||
labels += labelTpl.SetContent(template.HTML(label)).GetContent()
|
||||
} else {
|
||||
labels += labelTpl.SetContent(template.HTML(label)).GetContent()
|
||||
}
|
||||
}
|
||||
return labels
|
||||
})
|
||||
info.AddField("Status", "status", db.Varchar).
|
||||
FieldDisplay(func(value types.FieldModel) interface{} {
|
||||
uuid := value.Row["uuid"].(string)
|
||||
return manager.GetNodeStatus(uuid)
|
||||
})
|
||||
info.AddField("Created at", "created_at", db.Datetime).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
t, err := time.Parse(time.RFC3339, model.Value)
|
||||
if err != nil {
|
||||
return model.Value
|
||||
}
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}).
|
||||
FieldFilterable(types.FilterType{FormType: form.DatetimeRange}).
|
||||
FieldSortable()
|
||||
info.AddField("Updated at", "updated_at", db.Datetime).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
t, err := time.Parse(time.RFC3339, model.Value)
|
||||
if err != nil {
|
||||
return model.Value
|
||||
}
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}).
|
||||
FieldFilterable(types.FilterType{FormType: form.DatetimeRange}).
|
||||
FieldSortable()
|
||||
|
||||
info.SetGetDataFn(func(param parameter.Parameters) ([]map[string]interface{}, int) {
|
||||
filters := make(map[string][]string, len(param.Fields))
|
||||
listFilters := make(map[string][]string, len(param.Fields)+2)
|
||||
listFilters["offset"] = []string{strconv.Itoa((param.PageInt - 1) * param.PageSizeInt)}
|
||||
listFilters["limit"] = []string{param.PageSize}
|
||||
for key, values := range param.Fields {
|
||||
if key == "__pk" {
|
||||
key = "uuid"
|
||||
} else {
|
||||
if strings.HasPrefix(key, "__") {
|
||||
continue
|
||||
}
|
||||
key = strings.TrimSuffix(key, "__goadmin")
|
||||
}
|
||||
filters[key] = values
|
||||
listFilters[key] = values
|
||||
}
|
||||
if param.SortField != "" {
|
||||
if param.SortType == "asc" {
|
||||
listFilters["sort_asc"] = []string{param.SortField}
|
||||
} else {
|
||||
listFilters["sort_desc"] = []string{param.SortField}
|
||||
}
|
||||
}
|
||||
nodes, err := manager.GetNodes(listFilters)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, 0
|
||||
}
|
||||
count, err := manager.GetNodesCount(filters)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, 0
|
||||
}
|
||||
result := make([]map[string]interface{}, 0, len(nodes))
|
||||
for _, node := range nodes {
|
||||
var data map[string]interface{}
|
||||
rawData, _ := json.Marshal(node)
|
||||
json.Unmarshal(rawData, &data)
|
||||
result = append(result, data)
|
||||
}
|
||||
return result, count
|
||||
})
|
||||
|
||||
info.SetDeleteFn(func(ids []string) error {
|
||||
for _, uuid := range ids {
|
||||
if _, err := manager.DeleteNode(uuid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
info.SetTable("nodes").SetTitle("Nodes").SetDescription("Nodes")
|
||||
|
||||
defaultUUID, _ := uuid.NewV4()
|
||||
formList := nodeTable.GetForm()
|
||||
formList.AddField("UUID", "uuid", db.Varchar, form.Text).
|
||||
FieldMust().
|
||||
FieldNotAllowEdit().
|
||||
FieldDefault(defaultUUID.String())
|
||||
formList.AddField("Name", "name", db.Varchar, form.Text).
|
||||
FieldMust()
|
||||
formList.AddField("Squads", "squad_ids", db.Varchar, form.Select).
|
||||
FieldMust().
|
||||
FieldOptions(squadOptions).
|
||||
FieldDisableWhenUpdate()
|
||||
|
||||
formList.SetInsertFn(func(values mForm.Values) (err error) {
|
||||
squadIDs := make([]int, len(values["squad_ids[]"]))
|
||||
for i, rawSquadID := range values["squad_ids[]"] {
|
||||
squadID, err := strconv.Atoi(rawSquadID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
squadIDs[i] = squadID
|
||||
}
|
||||
_, err = manager.CreateNode(CM.NodeCreate{
|
||||
UUID: values.Get("uuid"),
|
||||
Name: values.Get("name"),
|
||||
SquadIDs: squadIDs,
|
||||
})
|
||||
return
|
||||
})
|
||||
|
||||
formList.SetUpdateFn(func(values mForm.Values) (err error) {
|
||||
uuid := values.Get("uuid")
|
||||
_, err = manager.UpdateNode(uuid, CM.NodeUpdate{
|
||||
Name: values.Get("name"),
|
||||
})
|
||||
return
|
||||
})
|
||||
|
||||
formList.SetTable("nodes").SetTitle("Nodes").SetDescription("Nodes")
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
164
service/admin_panel/tables/squad.go
Normal file
164
service/admin_panel/tables/squad.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package tables
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/GoAdminGroup/go-admin/context"
|
||||
"github.com/GoAdminGroup/go-admin/modules/db"
|
||||
mForm "github.com/GoAdminGroup/go-admin/plugins/admin/modules/form"
|
||||
"github.com/GoAdminGroup/go-admin/plugins/admin/modules/parameter"
|
||||
"github.com/GoAdminGroup/go-admin/plugins/admin/modules/table"
|
||||
"github.com/GoAdminGroup/go-admin/template/types"
|
||||
"github.com/GoAdminGroup/go-admin/template/types/form"
|
||||
"github.com/go-playground/validator/v10"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
)
|
||||
|
||||
func SquadTableFactory(manager CM.Manager, logger log.Logger) func(ctx *context.Context) (squadTable table.Table) {
|
||||
return func(ctx *context.Context) (squadTable table.Table) {
|
||||
squadTable = table.NewDefaultTable(ctx, table.Config{
|
||||
CanAdd: true,
|
||||
Editable: true,
|
||||
Deletable: true,
|
||||
Exportable: true,
|
||||
PrimaryKey: table.PrimaryKey{
|
||||
Type: db.Int,
|
||||
Name: table.DefaultPrimaryKeyName,
|
||||
},
|
||||
})
|
||||
|
||||
info := squadTable.GetInfo().SetFilterFormLayout(form.LayoutFilter)
|
||||
info.AddField("ID", "id", db.Int).
|
||||
FieldSortable()
|
||||
info.AddField("Name", "name", db.Varchar).
|
||||
FieldFilterable().
|
||||
FieldSortable()
|
||||
info.AddField("Created At", "created_at", db.Datetime).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
t, err := time.Parse(time.RFC3339, model.Value)
|
||||
if err != nil {
|
||||
return model.Value
|
||||
}
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}).
|
||||
FieldSortable().
|
||||
FieldFilterable(types.FilterType{FormType: form.DatetimeRange})
|
||||
info.AddField("Updated At", "updated_at", db.Datetime).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
t, err := time.Parse(time.RFC3339, model.Value)
|
||||
if err != nil {
|
||||
return model.Value
|
||||
}
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}).
|
||||
FieldSortable().
|
||||
FieldFilterable(types.FilterType{FormType: form.DatetimeRange})
|
||||
|
||||
info.SetGetDataFn(func(param parameter.Parameters) ([]map[string]interface{}, int) {
|
||||
filters := make(map[string][]string, len(param.Fields))
|
||||
listFilters := make(map[string][]string, len(param.Fields)+2)
|
||||
listFilters["offset"] = []string{strconv.Itoa((param.PageInt - 1) * param.PageSizeInt)}
|
||||
listFilters["limit"] = []string{param.PageSize}
|
||||
for key, values := range param.Fields {
|
||||
if key == "__pk" {
|
||||
key = "pk"
|
||||
} else if strings.HasPrefix(key, "__") {
|
||||
continue
|
||||
} else {
|
||||
key = strings.TrimSuffix(key, "__goadmin")
|
||||
}
|
||||
filters[key] = values
|
||||
listFilters[key] = values
|
||||
}
|
||||
if param.SortField != "" {
|
||||
if param.SortType == "asc" {
|
||||
listFilters["sort_asc"] = []string{param.SortField}
|
||||
} else {
|
||||
listFilters["sort_desc"] = []string{param.SortField}
|
||||
}
|
||||
}
|
||||
squads, err := manager.GetSquads(listFilters)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, 0
|
||||
}
|
||||
count, err := manager.GetSquadsCount(filters)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, 0
|
||||
}
|
||||
result := make([]map[string]interface{}, 0, len(squads))
|
||||
for _, squad := range squads {
|
||||
var data map[string]interface{}
|
||||
rawData, _ := json.Marshal(squad)
|
||||
json.Unmarshal(rawData, &data)
|
||||
result = append(result, data)
|
||||
}
|
||||
return result, count
|
||||
})
|
||||
|
||||
info.SetDeleteFn(func(ids []string) error {
|
||||
for _, id := range ids {
|
||||
intID, err := strconv.Atoi(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := manager.DeleteSquad(intID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
info.SetTable("squads").SetTitle("Squads").SetDescription("Squads")
|
||||
|
||||
formList := squadTable.GetForm()
|
||||
formList.AddField("ID", "id", db.Int, form.Default).
|
||||
FieldNotAllowAdd().
|
||||
FieldNotAllowEdit()
|
||||
formList.AddField("Name", "name", db.Varchar, form.Text).
|
||||
FieldMust()
|
||||
|
||||
formList.SetInsertFn(func(values mForm.Values) (err error) {
|
||||
_, err = manager.CreateSquad(CM.SquadCreate{
|
||||
Name: values.Get("name"),
|
||||
})
|
||||
if err != nil {
|
||||
if ve, ok := err.(validator.ValidationErrors); ok {
|
||||
var errors []string
|
||||
for _, e := range ve {
|
||||
switch e.Tag() {
|
||||
case "required":
|
||||
errors = append(errors, e.StructField()+": required field missing")
|
||||
default:
|
||||
errors = append(errors, e.StructField()+": invalid request")
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("%s", strings.Join(errors, "<br>"))
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
|
||||
formList.SetUpdateFn(func(values mForm.Values) (err error) {
|
||||
id, err := strconv.Atoi(values.Get("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = manager.UpdateSquad(id, CM.SquadUpdate{
|
||||
Name: values.Get("name"),
|
||||
})
|
||||
return
|
||||
})
|
||||
|
||||
formList.SetTable("squads").SetTitle("Squads").SetDescription("Squads")
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
282
service/admin_panel/tables/user.go
Normal file
282
service/admin_panel/tables/user.go
Normal file
@@ -0,0 +1,282 @@
|
||||
package tables
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/GoAdminGroup/go-admin/context"
|
||||
"github.com/GoAdminGroup/go-admin/modules/db"
|
||||
mForm "github.com/GoAdminGroup/go-admin/plugins/admin/modules/form"
|
||||
"github.com/GoAdminGroup/go-admin/plugins/admin/modules/parameter"
|
||||
"github.com/GoAdminGroup/go-admin/plugins/admin/modules/table"
|
||||
"github.com/GoAdminGroup/go-admin/template"
|
||||
"github.com/GoAdminGroup/go-admin/template/types"
|
||||
"github.com/GoAdminGroup/go-admin/template/types/form"
|
||||
"github.com/go-playground/validator/v10"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
CM "github.com/sagernet/sing-box/service/manager/constant"
|
||||
)
|
||||
|
||||
func UserTableFactory(manager CM.Manager, logger log.Logger) func(ctx *context.Context) (userTable table.Table) {
|
||||
return func(ctx *context.Context) (userTable table.Table) {
|
||||
userTable = table.NewDefaultTable(ctx, table.Config{
|
||||
CanAdd: true,
|
||||
Editable: true,
|
||||
Deletable: true,
|
||||
Exportable: true,
|
||||
PrimaryKey: table.PrimaryKey{
|
||||
Type: db.Int,
|
||||
Name: table.DefaultPrimaryKeyName,
|
||||
},
|
||||
})
|
||||
squads, err := manager.GetSquads(map[string][]string{})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
squadsByID := make(map[int]string, len(squads))
|
||||
squadOptions := make(types.FieldOptions, len(squads))
|
||||
for i, squad := range squads {
|
||||
squadsByID[squad.ID] = squad.Name
|
||||
squadOptions[i] = types.FieldOption{
|
||||
Text: squad.Name,
|
||||
Value: strconv.Itoa(squad.ID),
|
||||
}
|
||||
}
|
||||
info := userTable.GetInfo().SetFilterFormLayout(form.LayoutFilter)
|
||||
info.AddField("ID", "id", db.Int).
|
||||
FieldSortable()
|
||||
info.AddField("Squads", "squad_ids", db.Varchar).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
values := model.Row["squad_ids"].([]interface{})
|
||||
labels := template.HTML("")
|
||||
labelTpl := label(ctx).SetType("success")
|
||||
labelValues := make([]string, len(values))
|
||||
for i, squadID := range values {
|
||||
labelValues[i] = squadsByID[int(squadID.(float64))]
|
||||
}
|
||||
for key, label := range labelValues {
|
||||
if key == len(labelValues)-1 {
|
||||
labels += labelTpl.SetContent(template.HTML(label)).GetContent()
|
||||
} else {
|
||||
labels += labelTpl.SetContent(template.HTML(label)).GetContent()
|
||||
}
|
||||
}
|
||||
return labels
|
||||
})
|
||||
info.AddField("Username", "username", db.Varchar).
|
||||
FieldFilterable().
|
||||
FieldSortable()
|
||||
info.AddField("Type", "type", db.Varchar).
|
||||
FieldFilterable(
|
||||
types.FilterType{
|
||||
FormType: form.SelectSingle,
|
||||
Options: types.FieldOptions{
|
||||
{Text: "Hysteria", Value: "hysteria"},
|
||||
{Text: "Hysteria2", Value: "hysteria2"},
|
||||
{Text: "Trojan", Value: "trojan"},
|
||||
{Text: "TUIC", Value: "tuic"},
|
||||
{Text: "VLESS", Value: "vless"},
|
||||
{Text: "VMess", Value: "vmess"},
|
||||
},
|
||||
},
|
||||
).
|
||||
FieldSortable()
|
||||
info.AddField("Inbound", "inbound", db.Varchar).FieldFilterable().
|
||||
FieldSortable()
|
||||
info.AddField("Created at", "created_at", db.Datetime).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
t, err := time.Parse(time.RFC3339, model.Value)
|
||||
if err != nil {
|
||||
return model.Value
|
||||
}
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}).
|
||||
FieldFilterable(types.FilterType{FormType: form.DatetimeRange}).
|
||||
FieldSortable()
|
||||
info.AddField("Updated at", "updated_at", db.Datetime).
|
||||
FieldDisplay(func(model types.FieldModel) interface{} {
|
||||
t, err := time.Parse(time.RFC3339, model.Value)
|
||||
if err != nil {
|
||||
return model.Value
|
||||
}
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}).
|
||||
FieldFilterable(types.FilterType{FormType: form.DatetimeRange}).
|
||||
FieldSortable()
|
||||
|
||||
info.SetGetDataFn(func(param parameter.Parameters) ([]map[string]interface{}, int) {
|
||||
filters := make(map[string][]string, len(param.Fields))
|
||||
listFilters := make(map[string][]string, len(param.Fields)+2)
|
||||
listFilters["offset"] = []string{strconv.Itoa((param.PageInt - 1) * param.PageSizeInt)}
|
||||
listFilters["limit"] = []string{param.PageSize}
|
||||
for key, values := range param.Fields {
|
||||
if key == "__pk" {
|
||||
key = "pk"
|
||||
} else {
|
||||
if strings.HasPrefix(key, "__") {
|
||||
continue
|
||||
}
|
||||
key = strings.TrimSuffix(key, "__goadmin")
|
||||
}
|
||||
filters[key] = values
|
||||
listFilters[key] = values
|
||||
}
|
||||
if param.SortField != "" {
|
||||
if param.SortType == "asc" {
|
||||
listFilters["sort_asc"] = []string{param.SortField}
|
||||
} else {
|
||||
listFilters["sort_desc"] = []string{param.SortField}
|
||||
}
|
||||
}
|
||||
users, err := manager.GetUsers(listFilters)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, 0
|
||||
}
|
||||
count, err := manager.GetUsersCount(filters)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, 0
|
||||
}
|
||||
result := make([]map[string]interface{}, 0, len(users))
|
||||
for _, user := range users {
|
||||
var data map[string]interface{}
|
||||
rawData, _ := json.Marshal(user)
|
||||
json.Unmarshal(rawData, &data)
|
||||
result = append(result, data)
|
||||
}
|
||||
return result, count
|
||||
})
|
||||
info.SetDeleteFn(func(ids []string) error {
|
||||
for _, id := range ids {
|
||||
value, err := strconv.Atoi(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := manager.DeleteUser(value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
info.SetTable("users").SetTitle("Users").SetDescription("Users")
|
||||
|
||||
formList := userTable.GetForm()
|
||||
formList.AddField("ID", "id", db.Int, form.Default).
|
||||
FieldNotAllowEdit().
|
||||
FieldNotAllowAdd()
|
||||
formList.AddField("Squads", "squad_ids", db.Varchar, form.Select).
|
||||
FieldMust().
|
||||
FieldOptions(squadOptions).
|
||||
FieldDisableWhenUpdate()
|
||||
formList.AddField("Username", "username", db.Varchar, form.Text).
|
||||
FieldMust().
|
||||
FieldDisplayButCanNotEditWhenUpdate()
|
||||
formList.AddField("Type", "type", db.Varchar, form.SelectSingle).
|
||||
FieldMust().
|
||||
FieldDisplayButCanNotEditWhenUpdate().
|
||||
FieldOptions(types.FieldOptions{
|
||||
{Text: "Hysteria", Value: "hysteria"},
|
||||
{Text: "Hysteria2", Value: "hysteria2"},
|
||||
{Text: "Trojan", Value: "trojan"},
|
||||
{Text: "TUIC", Value: "tuic"},
|
||||
{Text: "VLESS", Value: "vless"},
|
||||
{Text: "VMess", Value: "vmess"},
|
||||
}).
|
||||
FieldOnChooseOptionsHide([]string{""}, "inbound").
|
||||
FieldOnChooseOptionsHide([]string{"", "hysteria", "hysteria2", "shadowsocks", "trojan", "tuic"}, "uuid").
|
||||
FieldOnChooseOptionsHide([]string{"", "vless", "vmess"}, "password").
|
||||
FieldOnChooseOptionsHide([]string{"", "hysteria", "hysteria2", "shadowsocks", "trojan", "tuic", "vmess"}, "flow").
|
||||
FieldOnChooseOptionsHide([]string{"", "hysteria", "hysteria2", "shadowsocks", "trojan", "tuic", "vless"}, "alter_id")
|
||||
formList.AddField("Inbound", "inbound", db.Varchar, form.Text).
|
||||
FieldMust().
|
||||
FieldDisplayButCanNotEditWhenUpdate().
|
||||
FieldOptionInitFn(func(val types.FieldModel) types.FieldOptions {
|
||||
return types.FieldOptions{
|
||||
{Value: val.Value, Text: val.Value, Selected: true},
|
||||
}
|
||||
})
|
||||
formList.AddField("UUID", "uuid", db.Varchar, form.Text)
|
||||
formList.AddField("Password", "password", db.Varchar, form.Text)
|
||||
formList.AddField("Flow", "flow", db.Varchar, form.SelectSingle).
|
||||
FieldOptions(types.FieldOptions{
|
||||
{Text: "xtls-rprx-vision", Value: "xtls-rprx-vision"},
|
||||
})
|
||||
formList.AddField("Alter ID", "alter_id", db.Varchar, form.Number).
|
||||
FieldDefault("0")
|
||||
|
||||
formList.SetInsertFn(func(values mForm.Values) (err error) {
|
||||
squadIDs := make([]int, len(values["squad_ids[]"]))
|
||||
for i, rawSquadID := range values["squad_ids[]"] {
|
||||
squadID, err := strconv.Atoi(rawSquadID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
squadIDs[i] = squadID
|
||||
}
|
||||
var alterId int
|
||||
if value := values.Get("alter_id"); value != "" {
|
||||
alterId, err = strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = manager.CreateUser(CM.UserCreate{
|
||||
SquadIDs: squadIDs,
|
||||
Username: values.Get("username"),
|
||||
Type: values.Get("type"),
|
||||
Inbound: values.Get("inbound"),
|
||||
UUID: values.Get("uuid"),
|
||||
Password: values.Get("password"),
|
||||
Flow: values.Get("flow"),
|
||||
AlterID: alterId,
|
||||
})
|
||||
if err != nil {
|
||||
if ve, ok := err.(validator.ValidationErrors); ok {
|
||||
var errors []string
|
||||
for _, e := range ve {
|
||||
switch e.Tag() {
|
||||
case "required":
|
||||
errors = append(errors, e.StructField()+": required field missing")
|
||||
case "uuid4":
|
||||
errors = append(errors, e.StructField()+": invalid UUID")
|
||||
default:
|
||||
errors = append(errors, e.StructField()+": invalid request")
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("%s", strings.Join(errors, "<br>"))
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
formList.SetUpdateFn(func(values mForm.Values) (err error) {
|
||||
id, err := strconv.Atoi(values.Get("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var alterId int
|
||||
if value := values.Get("alter_id"); value != "" {
|
||||
alterId, err = strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = manager.UpdateUser(id, CM.UserUpdate{
|
||||
UUID: values.Get("uuid"),
|
||||
Password: values.Get("password"),
|
||||
Flow: values.Get("flow"),
|
||||
AlterID: alterId,
|
||||
})
|
||||
return
|
||||
})
|
||||
|
||||
formList.SetTable("users").SetTitle("Users").SetDescription("Users")
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user