Add CI/CD configuration and API documentation

This commit is contained in:
2026-07-01 21:40:53 +08:00
commit c590135d68
4168 changed files with 740252 additions and 0 deletions
@@ -0,0 +1,76 @@
import Foundation
import Library
public enum ProfileUpdateTask {
static let minUpdateInterval: TimeInterval = 15 * 60
static let defaultUpdateInterval: TimeInterval = 60 * 60
private static var timer: Timer?
public static func configure() async throws {
timer?.invalidate()
timer = nil
let profiles = try await ProfileManager.listAutoUpdateEnabled()
if profiles.isEmpty {
return
}
var updateInterval = profiles.map { it in
it.autoUpdateIntervalOrDefault
}.min()!
if updateInterval < minUpdateInterval {
updateInterval = minUpdateInterval
}
timer = Timer(fire: calculateEarliestBeginDate(profiles), interval: updateInterval, repeats: true) { _ in
Task {
await getAndupdateProfiles()
}
}
}
static func calculateEarliestBeginDate(_ profiles: [Profile]) -> Date {
let nowTime = Date.now
var earliestBeginDate = profiles.map { it in
it.lastUpdated!.addingTimeInterval(it.autoUpdateIntervalOrDefault)
}.min()!
if earliestBeginDate <= nowTime {
earliestBeginDate = nowTime
}
return earliestBeginDate
}
private nonisolated static func getAndupdateProfiles() async {
do {
_ = try await updateProfiles(ProfileManager.listAutoUpdateEnabled())
NSLog("profile update task succeed")
} catch {
NSLog("profile update task failed: \(error.localizedDescription)")
}
}
static func updateProfiles(_ profiles: [Profile]) async -> Bool {
var success = true
for profile in profiles {
if profile.lastUpdated! > Date(timeIntervalSinceNow: -profile.autoUpdateIntervalOrDefault) {
continue
}
do {
try await profile.updateRemoteProfile()
NSLog("Updated profile \(profile.name)")
} catch {
NSLog("Update profile \(profile.name) failed: \(error.localizedDescription)")
success = false
}
}
return success
}
}
extension Profile {
var autoUpdateIntervalOrDefault: TimeInterval {
if autoUpdateInterval > 0 {
return TimeInterval(autoUpdateInterval * 60)
} else {
return ProfileUpdateTask.defaultUpdateInterval
}
}
}
@@ -0,0 +1,139 @@
//
// File.swift
// LoginKit
//
// Created by Mac on 2024/10/12.
//
import Foundation
import Foundation
//MACOS
// UserManager UserDefaults
class StoreManager {
static let shared = StoreManager()
public let configURL = "https://api.gooapis.com/api/vpnconfig.php"
// UserDefaults keys
private let loginStatusKey = "isLoggedIn"
private let userEmailKey = "userEmail"
private let avatarurlKey = "avatar_url"
private let autodataKey = "autodata"
private let subURLDataKey = "subURLData"
private let baseURLKey = "baseURLKey"
// UserDefaults
private let defaults = UserDefaults.standard
func baseURL()-> String{
return UserDefaults.standard.string(forKey: "baseURLKey") ?? ""
}
func mainregisterURL()-> String{
UserDefaults.standard.string(forKey: "mainregisterURLKey") ?? ""
}
func paymentURL()-> String{
UserDefaults.standard.string(forKey: "paymentURLKey") ?? ""
}
func telegramUrl()-> String{
UserDefaults.standard.string(forKey: "telegramUrlKey") ?? ""
}
func kefuUrl()-> String{
UserDefaults.standard.string(forKey: "kefuUrlKey") ?? ""
}
func websiteURL()-> String{
UserDefaults.standard.string(forKey: "websiteURLKey") ?? ""
}
// URL
func storebaseURLData(data: String) {
defaults.set(data, forKey: "baseURLKey")
defaults.synchronize()
}
func storemainregisterURLData(data: String) {
defaults.set(data, forKey: "mainregisterURLKey")
defaults.synchronize()
}
func storepaymentURLData(data: String) {
defaults.set(data, forKey: "paymentURLKey")
defaults.synchronize()
}
func storetelegramUrlData(data: String) {
defaults.set(data, forKey: "telegramUrlKey")
defaults.synchronize()
}
func storekefuUrlData(data: String) {
defaults.set(data, forKey: "kefuUrlKey")
defaults.synchronize()
}
func storewebsiteURLData(data: String) {
defaults.set(data, forKey: "websiteURLKey")
defaults.synchronize()
}
//
func updateLoginStatus(_ loggedIn: Bool) {
defaults.set(loggedIn, forKey: loginStatusKey)
defaults.synchronize()
}
//
func isUserLoggedIn() -> Bool {
return defaults.bool(forKey: loginStatusKey)
}
//
func storeUserInfo(email: String, avator: String) {
defaults.set(email, forKey: userEmailKey)
defaults.set(avator, forKey: avatarurlKey) // info JSON
defaults.synchronize()
}
//
func getUserInfo() -> (email: String, avator: String) {
let email = defaults.string(forKey: userEmailKey) ?? ""
let avator = defaults.string(forKey: avatarurlKey) ?? ""
return (email, avator)
}
// autodata
func storeAutoData(data: String) {
defaults.set(data, forKey: autodataKey)
defaults.synchronize()
}
// autodata
func getSuburlData() -> String {
return defaults.string(forKey: subURLDataKey) ?? ""
}
// autodata
func storeSuburlData(data: String) {
defaults.set(data, forKey: subURLDataKey)
defaults.synchronize()
}
// autodata
func getAutoData() -> String {
return defaults.string(forKey: autodataKey) ?? ""
}
//
func clearUserData() {
defaults.removeObject(forKey: loginStatusKey)
defaults.removeObject(forKey: userEmailKey)
defaults.removeObject(forKey: avatarurlKey)
defaults.removeObject(forKey: autodataKey)
defaults.removeObject(forKey: subURLDataKey)
}
}
@@ -0,0 +1,79 @@
import BackgroundTasks
import Foundation
import Library
#if canImport(UIKit)
import UIKit
#endif
#if os(iOS) || os(tvOS)
public class UIProfileUpdateTask: BGAppRefreshTask {
private static let taskSchedulerPermittedIdentifier = "\(FilePath.packageName).update_profiles"
private static var registered = false
public static func configure() throws {
if !registered {
let success = BGTaskScheduler.shared.register(forTaskWithIdentifier: taskSchedulerPermittedIdentifier, using: nil) { task in
NSLog("profile update task started")
Task {
await UIProfileUpdateTask.getAndUpdateProfiles(task)
}
}
if !success {
throw NSError(domain: "register task failed", code: 0)
}
registered = true
}
Task {
BGTaskScheduler.shared.cancelAllTaskRequests()
let profiles = try await ProfileManager.listAutoUpdateEnabled()
if profiles.isEmpty {
return
}
try scheduleUpdate(ProfileUpdateTask.calculateEarliestBeginDate(profiles))
}
Task {
if await UIApplication.shared.backgroundRefreshStatus != .available {
await updateOnce()
}
}
}
private nonisolated static func updateOnce() async {
NSLog("update profiles at start since background refresh unavailable")
let profiles: [Profile]
do {
profiles = try await ProfileManager.listAutoUpdateEnabled()
} catch {
return
}
if profiles.isEmpty {
return
}
_ = await ProfileUpdateTask.updateProfiles(profiles)
}
private nonisolated static func getAndUpdateProfiles(_ task: BGTask) async {
let profiles: [Profile]
do {
profiles = try await ProfileManager.listAutoUpdateEnabled()
} catch {
return
}
if profiles.isEmpty {
return
}
let success = await ProfileUpdateTask.updateProfiles(profiles)
try? scheduleUpdate(ProfileUpdateTask.calculateEarliestBeginDate(profiles))
task.setTaskCompleted(success: success)
task.expirationHandler = {
try? scheduleUpdate(nil)
}
}
private static func scheduleUpdate(_ earliestBeginDate: Date?) throws {
let request = BGAppRefreshTaskRequest(identifier: taskSchedulerPermittedIdentifier)
request.earliestBeginDate = earliestBeginDate
try BGTaskScheduler.shared.submit(request)
}
}
#endif