123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512 |
- //
- // account.swift
- // 74 桌
- //
- // Created by yunli on 2023/5/20.
- //
- import Alamofire
- import SwiftUI
- struct Constant {
- var cookiesDefaultsKey: String
- }
- let Constants: Constant = .init(cookiesDefaultsKey: "JSESSIONID")
- class CookieHandler {
- static let shared: CookieHandler = .init()
- let defaults = UserDefaults.standard
- let cookieStorage = HTTPCookieStorage.shared
- func getCookie(forURL url: String) -> [HTTPCookie] {
- let computedUrl = URL(string: url)
- let cookies = cookieStorage.cookies(for: computedUrl!) ?? []
- return cookies
- }
- func getAllCookie() -> [HTTPCookie] {
- let cookies = cookieStorage.cookies ?? []
- return cookies
- }
- func delCookie() {}
- }
- struct Login: Encodable {
- let user: String
- let pwd: String
- let cook: String
- }
- struct UserInfo: Decodable {
- var name: String
- var oid: String
- var id: String
- var xydLink: String
- var serviceLink: String
- var studyLink: String
- var tableData: [PurpleDatum]
- init() {
- name = "-"
- oid = "-"
- id = "-"
- xydLink = "-"
- serviceLink = "-"
- studyLink = "-"
- tableData = []
- }
- }
- class LoginHandler {
- static let sharedInstance = LoginHandler()
- func getCookie(name: String) -> [HTTPCookie] {
- return CookieHandler().getCookie(forURL: "https://debug.sdsz.icu:81/").filter { cookie -> Bool in
- cookie.name == name
- }
- }
- func getAllCookie(name: String) -> [HTTPCookie] {
- return CookieHandler().getAllCookie().filter { cookie -> Bool in
- cookie.name == name
- }
- }
- func fetchLoginCookie(action: @escaping (_: String) -> Void) {
- if let cookie = getCookie(name: "JSESSIONID").first {
- HTTPCookieStorage.shared.deleteCookie(cookie)
- print(cookie)
- }
- let url = "https://debug.sdsz.icu:81/sso/login"
- AF.request(url, method: .get, parameters: nil, encoding: URLEncoding.default, headers: nil).response { _ in
- if let cookie = self.getCookie(name: "JSESSIONID").first {
- action(cookie.value)
- }
- }
- }
- }
- class FetchHandler {
- static let sharedInstance = FetchHandler()
- func matches(url: String) -> Bool {
- do {
- return try url.matches(of: Regex(#"(^\[\]$|param":"|"workRests"|<tr>|DOCTYPE|in\?username=|requestParams|\"newmessage\"|Sorry, Page Not Found|北师大实验中学--登录|404 Not Found|502 Bad Gateway)"#)).count != 0
- } catch {
- return true
- }
- }
- func getUrl(url: String) -> String {
- var ret: String = url
- ret = ret
- .replacing("dd.sdsz.com.cn", with: "debug.sdsz.icu:81")
- .replacing(/^http:/, with: "https:")
- .replacing("service=http%3A%2F%2Fdebug.sdsz.icu:81", with: "service=http%3A%2F%2Fdd.sdsz.com.cn")
- print("URL: \(ret)")
- return ret
- }
- func workFetch(url: String, action: @escaping (_: String) -> Void) {
- if matches(url: url) {
- action(url)
- } else {
- AF.request(getUrl(url: url), method: .get, parameters: nil, encoding: URLEncoding.default, headers: nil).responseString { res in
- if let resv = res.value {
- self.workFetch(url: resv, action: action)
- }
- }
- }
- }
- func fetchAny(url: String, action: @escaping (_: String) -> Void) {
- let urlFull = "https://debug.sdsz.icu:81/\(url)"
- workFetch(url: urlFull, action: action)
- }
- }
- func doLogin(user: String, password: String, action: @escaping (_: Int) -> Void) {
- LoginHandler().fetchLoginCookie { (cookie: String) in
- let login = Login(user: user, pwd: password, cook: cookie)
- print(login)
- AF.request("https://debug.sdsz.icu/andlogin", method: .post, parameters: login, encoder: JSONParameterEncoder.default, headers: nil).responseString { res in
- print("\(res)")
- if res.value == "success" {
- action(1)
- } else {
- action(0)
- }
- }
- }
- }
- // This file was generated from JSON Schema using quicktype, do not modify it directly.
- // To parse the JSON, add this file to your project and do:
- //
- // let welcome = try? JSONDecoder().decode(Welcome.self, from: jsonData)
- import Foundation
- // MARK: - Welcome
- struct Welcome: Codable {
- let workRests: [WorkREST]
- let blocks: [Block]
- }
- // MARK: - Block
- struct Block: Codable {
- let ownObjID, type: String
- let name: Name
- let data: [BlockDatum]
- let weekMetas: JSONNull?
- enum CodingKeys: String, CodingKey {
- case ownObjID = "ownObjId"
- case type, name, data, weekMetas
- }
- }
- // MARK: - BlockDatum
- struct BlockDatum: Codable {
- let week: Int
- let weekMeta: WeekMeta
- let data: [PurpleDatum]
- }
- // MARK: - PurpleDatum
- struct PurpleDatum: Codable {
- let day, lesson: Int
- let data: [FluffyDatum]
- }
- // MARK: - FluffyDatum
- struct FluffyDatum: Codable {
- let timetableID: JSONNull?
- let className: Name
- let classID: ClassID
- let courseName: String
- let courseID, courseType: Int
- let placeID: JSONNull?
- let placeName: PlaceName
- let placeSn: PlaceSn
- let teacher: [Teacher]
- let room: JSONNull?
- let start, end: String
- let timeScope: Int
- enum CodingKeys: String, CodingKey {
- case timetableID = "timetableId"
- case className
- case classID = "classId"
- case courseName
- case courseID = "courseId"
- case courseType
- case placeID = "placeId"
- case placeName, placeSn, teacher, room, start, end, timeScope
- }
- }
- enum ClassID: String, Codable {
- case ac342897 = "AC_342897"
- case tac8109 = "TAC_8109"
- case tac8123 = "TAC_8123"
- }
- enum Name: String, Codable {
- case 高一数学P1班 = "高一数学[P]1班"
- case 高一物理A5班 = "高一物理[A]5班"
- case 高中2025届11班
- }
- enum PlaceName: String, Codable {
- case 信毅楼408 = "信毅楼4-08"
- case 信毅楼412 = "信毅楼4-12"
- case 本班教室
- }
- enum PlaceSn: String, Codable {
- case bxXy408 = " BX-XY-408 "
- case bxXy410 = " BX-XY-410 "
- case empty = ""
- }
- // MARK: - Teacher
- struct Teacher: Codable {
- let userID: Int
- let userName: JSONNull?
- let fullName: String
- let userNo: JSONNull?
- enum CodingKeys: String, CodingKey {
- case userID = "userId"
- case userName, fullName, userNo
- }
- }
- // MARK: - WeekMeta
- struct WeekMeta: Codable {
- let morningRead: Bool
- let night, afternoon, morning, teachingDay: Int
- let time: [Time]
- }
- // MARK: - Time
- struct Time: Codable {
- let seq: Int
- let start, end: String
- }
- // MARK: - WorkREST
- struct WorkREST: Codable {
- let beginTime, endTime, time, lessonName: String
- }
- // MARK: - Encode/decode helpers
- class JSONNull: Codable, Hashable {
- public static func == (_: JSONNull, _: JSONNull) -> Bool {
- return true
- }
- func hash(into _: inout Hasher) {}
- public init() {}
- public required init(from decoder: Decoder) throws {
- let container = try decoder.singleValueContainer()
- if !container.decodeNil() {
- throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
- }
- }
- public func encode(to encoder: Encoder) throws {
- var container = encoder.singleValueContainer()
- try container.encodeNil()
- }
- }
- var Count: Int = 0
- func addCompleted(userInfo: UserInfo, action: @escaping (_: UserInfo) -> Void) {
- Count += 1
- if Count == 3 {
- print(userInfo.tableData)
- action(userInfo)
- }
- }
- extension String {
- func replace(regex: String, with: String, options: NSRegularExpression.Options) -> String {
- do {
- let RE = try NSRegularExpression(pattern: regex, options: options)
- let modified = RE.stringByReplacingMatches(in: self, range: NSRange(location: 0, length: count), withTemplate: with)
- return modified
- } catch {
- return "ERR"
- }
- }
- func replace(regex: String, with: String) -> String {
- do {
- let RE = try NSRegularExpression(pattern: regex)
- let modified = RE.stringByReplacingMatches(in: self, range: NSRange(location: 0, length: count), withTemplate: with)
- return modified
- } catch {
- return "ERR"
- }
- }
- }
- struct TableParam: Encodable {
- var oid: String
- }
- func getUserInfo(userInfoIn: UserInfo, action: @escaping (_: UserInfo) -> Void) {
- var userInfo: UserInfo = userInfoIn
- FetchHandler().fetchAny(url: "bxn-portal/portal/osforstudent/index") { res in
- if let mat = res.firstMatch(of: /userFullName" value="(.*?)"/) {
- userInfo.name = "\(mat.1)"
- }
- if let mat = res.firstMatch(of: /userId" value="(.*?)"/) {
- userInfo.oid = "\(mat.1)"
- }
- if let mat = res.firstMatch(of: /'https:\/\/service.*?'/) {
- userInfo.serviceLink = "\(mat.output.split(separator: "'")[0])"
- userInfo.studyLink = mat.output.split(separator: "'")[0] + "%2Ffe-pc%2Fb%2Ffe_leco_student%2Fportal%2F%3Fsystem_partition_gId%3D3"
- }
- print(userInfo)
- let tableParam: TableParam = .init(oid: userInfo.oid)
- let header: HTTPHeaders = [
- "user": userInfo.id,
- ]
- AF.request("https://debug.sdsz.icu/getWeek", method: .post, parameters: tableParam, encoder: JSONParameterEncoder.default, headers: header).responseString { res in
- FetchHandler().fetchAny(url: "bxn-timetable/timetable/monitor/homepage/data/student?studentId=\(userInfo.oid)&dateScope=\(res.value ?? "")&_=0") { res in
- do {
- let data = res.data(using: String.Encoding.utf8)
- let table = try! JSONSerialization.jsonObject(with: data!) as? [String: Any]
- let decoder = JSONDecoder()
- let kkd = try decoder.decode(Welcome.self, from: data!)
- userInfo.tableData = kkd.blocks.first?.data.first?.data ?? []
- } catch {
- print(error)
- }
- addCompleted(userInfo: userInfo, action: action)
- }
- }
- addCompleted(userInfo: userInfo, action: action)
- }
- FetchHandler().fetchAny(url: "bxn-library/library/jumpExamreport?jumpUrl=http://36.112.23.77/analysis/auto/%23/autoLogin") { res in
- userInfo.xydLink = res
- addCompleted(userInfo: userInfo, action: action)
- }
- }
- struct loginView: View {
- @State var username: String = ""
- @State var password: String = ""
- @State var btnText: String = "登录"
- @State var btnColor: Color = .blue
- @State var inProgress: Bool = false
- @FocusState private var isFocused: Bool
- @Binding var isLoggedIn: Int
- @Binding var userInfo: UserInfo
- var body: some View {
- VStack {
- Form {
- List {
- HStack {
- Image(systemName: "person.fill").foregroundColor(Color(red: 0.7, green: 0.7, blue: 0.7))
- TextField(text: $username, prompt: Text("数字校园号")) {
- Text("数字校园号")
- }.focused($isFocused)
- .keyboardType(.numberPad)
- }
- HStack {
- Image(systemName: "key.fill").foregroundColor(Color(red: 0.7, green: 0.7, blue: 0.7))
- SecureField(text: $password, prompt: Text("密码")) {
- Text("密码")
- }.focused($isFocused)
- }
- }.onTapGesture {
- isFocused = false
- }
- HStack {
- Spacer()
- Button(action: {
- inProgress = true
- doLogin(user: username, password: password) { (ret: Int) in
- isLoggedIn = ret
- inProgress = false
- if ret == 0 {
- btnText = "登录失败"
- btnColor = .red
- } else {
- userInfo.id = username
- getUserInfo(userInfoIn: userInfo) { res in
- userInfo = res
- }
- }
- }
- }) {
- HStack {
- Text(btnText)
- if inProgress {
- ProgressView()
- } else {
- Image(systemName: "chevron.right")
- }
- }
- }.foregroundColor(btnColor).buttonStyle(.bordered)
- }
- }
- }
- }
- }
- struct accountView: View {
- @State var username: String = ""
- @State var password: String = ""
- @FocusState private var isFocused: Bool
- @Binding var isLoggedIn: Int
- @Binding var userInfo: UserInfo
- func delAllCookie(name: String) {
- for cookie in LoginHandler().getAllCookie(name: name) {
- print(cookie)
- HTTPCookieStorage.shared.deleteCookie(cookie)
- }
- }
- var body: some View {
- VStack {
- Form {
- Section(header: Text("基本信息")) {
- HStack {
- Text("姓名")
- Spacer()
- Text(userInfo.name).foregroundColor(.gray)
- }
- HStack {
- Text("内部 ID")
- Spacer()
- Text(userInfo.oid).foregroundColor(.gray)
- }
- }
- Section {
- NavigationLink(destination: aboutView()) {
- Text("关于")
- }
- }
- Section {
- Button(action: {
- isLoggedIn = 0
- delAllCookie(name: "CASTGC")
- delAllCookie(name: "JSESSIONID")
- }) {
- VStack {
- Text("退出").foregroundColor(.red)
- }
- }
- }
- }
- Spacer()
- }
- }
- }
- struct aboutView: View {
- var body: some View {
- List {
- Section(footer: Text("实验中学 74 桌是一款面向北师大附属实验中学学生的 App,提供查分及数字校园基础服务。")) {
- Text("实验中学 74 桌")
- HStack {
- Text("开发者")
- Spacer()
- Text("74 开发组").foregroundColor(.gray)
- }
- }
- }.navigationBarTitle("关于")
- }
- }
- struct accountView_Previews: PreviewProvider {
- @State static var isLoggedIn = 1
- @State static var userInfo: UserInfo = .init()
- static var previews: some View {
- loginView(isLoggedIn: $isLoggedIn, userInfo: $userInfo)
- }
- }
|