package share import ( "context" "sort" "strings" "amnezia-share/internal/models" "amnezia-share/internal/settings" ) func containsStr(list []string, needle string) bool { for _, s := range list { if s == needle { return true } } return false } // ServerSortRank ranks a server for guest-page display: WireGuard-capable first, // then AWG2-capable, then VLESS-capable, then everything else; disabled servers // always sort last. func ServerSortRank(s models.ServerInfo) int { if s.Disabled { return 3 } protos := s.Protocols if len(protos) == 0 { protos = DefaultServerProtocols } switch { case containsStr(protos, "wireguard"): return 0 case containsStr(protos, "awg2"): return 1 case containsStr(protos, "vless"): return 2 default: return 3 } } // SortServersWireguardFirst orders servers by ServerSortRank, then by label // (case-insensitive) within the same rank. func SortServersWireguardFirst(servers []models.ServerInfo) []models.ServerInfo { sort.SliceStable(servers, func(i, j int) bool { ri, rj := ServerSortRank(servers[i]), ServerSortRank(servers[j]) if ri != rj { return ri < rj } li := servers[i].Label if li == "" { li = servers[i].Name } lj := servers[j].Label if lj == "" { lj = servers[j].Name } return strings.ToLower(li) < strings.ToLower(lj) }) return servers } // FilterServersForLink drops servers not present in the link's allowed_server_ids // whitelist (nil whitelist = no filtering). func FilterServersForLink(link models.ShareLink, servers []models.ServerInfo) []models.ServerInfo { allowed := ParseAllowedServerIDs(link.AllowedServerIDs) if allowed == nil { return servers } set := make(map[int]bool, len(allowed)) for _, id := range allowed { set[id] = true } out := make([]models.ServerInfo, 0, len(servers)) for _, s := range servers { if set[s.ID] { out = append(out, s) } } return out } // ServerProtocolAllowed reports whether protocol may be used on serverID, per the // admin-configured per-server protocol map. Servers absent from that map default // to allowing only wireguard/awg2 (matches amnezia_server_protocol_allowed()). func ServerProtocolAllowed(ctx context.Context, st *settings.Store, serverID int, protocol string) bool { protosMap := st.JSONMapStringSlice(ctx, settings.KeyProtocolsJSON) if list, ok := protosMap[serverID]; ok { return containsStr(list, protocol) } return protocol == "wireguard" || protocol == "awg2" } // BuildGuestServers assembles the guest-facing server catalog from admin settings: // labels (id -> display name, normally pre-merged from env/DB/panel_server_labels), // per-server protocol whitelist (default: wireguard+awg2), flags, speeds, traffic // notices and the disabled-server list. The result is sorted WireGuard-first. func BuildGuestServers(ctx context.Context, st *settings.Store, labels map[int]string) []models.ServerInfo { protosMap := st.JSONMapStringSlice(ctx, settings.KeyProtocolsJSON) flagsMap := st.JSONMapString(ctx, settings.KeyFlagsJSON) speedsMap := st.JSONMapString(ctx, settings.KeySpeedsJSON) noticesMap := st.JSONMapString(ctx, settings.KeyTrafficNotices) disabled := st.DisabledServers(ctx) ids := make([]int, 0, len(labels)) for id := range labels { ids = append(ids, id) } sort.Ints(ids) out := make([]models.ServerInfo, 0, len(ids)) for _, id := range ids { name := strings.TrimSpace(labels[id]) if name == "" { continue } protocols := protosMap[id] if len(protocols) == 0 { protocols = append([]string(nil), DefaultServerProtocols...) } out = append(out, models.ServerInfo{ ID: id, Label: name, Name: name, Protocols: protocols, Disabled: disabled[id], Flag: flagsMap[id], Speed: speedsMap[id], Notice: noticesMap[id], }) } return SortServersWireguardFirst(out) }