import Vapor import Fluent struct AuthController: RouteCollection { func boot(routes: any RoutesBuilder) throws { routes.post("v4", "device", "accessToken", "create", use: self.createAccessToken) routes.post("v3", "agreement", "getForLogin", use: self.loginAgreement) routes.post("v4", "auth", "loginDevice", use: self.loginDevice) } @Sendable func createAccessToken(req: Request) async throws -> AccessTokenRes { AccessTokenRes( accessToken: "fwPla7fQ8ty9+DZT/lD//uWZD4uD6C4lD6gGIIZTLKRTQ52/SLCRmk/370jcWGs+e+1iSoZtL7lj8ov9B0/jHmijH4nsHPQT6pchaQM1M9mtwYNQq0BWhVr9hF0jjCK/a5LIVd1kBac/Gemv29WKEDKSrUS9HxxUigoPRwtOy8m+oDj9FmDJZ+rzqWCc0QjES4Ky0fTpXZ7ESoguDzNmRtW3FYr+OFexw8wBPlwiC4w=", expiryTime: Int(Date.init(timeIntervalSinceNow: .init(0)).timeIntervalSince1970) ) } @Sendable func loginAgreement(req: Request) async throws -> String { let body = try req.content.decode(LoginAgreementReq.self, as: .json) return #""" { "adAgreementStatus": "n", "agreement": { "E001": "n", "E002": "n", "E006": "n", "N002": "n", "N003": "n", "timestamp": "\#(Int(Date().timeIntervalSince1970) * 1000)" }, "agreementPopup": "n", "appId": "\#(body.appId)", "appName": "World Flipper (NA)", "context": "login", "country": "\#(body.country)", "firstAgreement": "n", "idpCode": "\#(body.idpCode)", "idpId": "6076008646", "informationSecurityCountry": "kr", "kakaoSyncAgreementGetSet": "n", "kakaoSyncStatus": "off", "kakaogameSdkVer": "3.0", "lang": "\#(body.lang)", "partnerId": 825, "partnerName": "주식회사 카카오게임즈", "plusFriendStatusInfo": null, "policyApplyTime": 1630854000000 } """# } @Sendable func loginDevice(req: Request) async throws -> Response { let body = try req.content.decode(LoginDeviceReq.self, as: .json) let idpAlias = generateIdpAlias(appId: body.appId, deviceId: body.deviceId, serialNo: body.serialNo) let idpId = body.whiteKey var account: Account? = nil if let rawAccountId = req.headers["playerId"].first, let accountId = Int(rawAccountId) { if let existingAccount = try await Account.query(on: req.db).filter(\.$id == accountId).first() { account = existingAccount } } else if let existingAccount = try await Account.query(on: req.db).filter(\.$idpId == idpId).first() { account = existingAccount } else { let account = Account(appId: body.appId, idpAlias: idpAlias, idpCode: "zd3", idpId: idpId, status: "normal") try await account.create(on: req.db) } guard let account = account else { return Response(status: .badRequest, body: "{\"error\": \"Bad Request\", \"message\": \"Invalid playerId provided.\"}") } let zatExpiry = Date.now.advanced(by: 43200) let zrtExpiry = Date.now.advanced(by: 2592000) let zatTokenJWT = generateToken(accountId: try account.requireID(), expires: zatExpiry, type: .ZAT) let zrtTokenJWT = generateToken(accountId: try account.requireID(), expires: zrtExpiry, type: .ZRT) let zatToken = try await req.jwt.sign(zatTokenJWT) let zrtToken = try await req.jwt.sign(zrtTokenJWT) let res = LoginDeviceRes( zatExpiryTime: Int(zatExpiry.timeIntervalSince1970) * 1000, zrtExpiryTime: Int(zrtExpiry.timeIntervalSince1970) * 1000, firstLogin: true, externalToken: "", zat: zatToken, zrt: zrtToken, player: Player( idpId: account.idpId, appId: account.appId, playerId: String(try account.requireID()), pushOption: PushOptionResponse(night: "n", player: "n"), regTime: Int(account.regDate.timeIntervalSince1970), idpAlias: idpAlias, firstLoginTime: Int(account.firstLogin.timeIntervalSince1970), status: account.status ) ) return try await res.encodeResponse(for: req) } } struct AccessTokenRes: Content { let accessToken: String let expiryTime: Int } struct LoginAgreementReq: Content { let deviceId: String let os: String let country: String let lang: String let appId: String let idpCode: String let serialNo: String let idpId: String } struct LoginDeviceReq: Content { let lang: String let clientTime: Int let deviceId: String let serialNo: String let country: String let whiteKey: String let market: String let appSecret: String let deviceAppKey: String let sdkVer: String let appVer: String let os: String let loginType: String let accessToken: String let resume: Bool let osVer: String let appId: String let deviceModel: String let network: String let isIosAppOnMac: Bool let adid: String let timezoneOffset: Int let fields: [String] let telecom: String } struct LoginDeviceRes: Content { let zatExpiryTime: Int let zrtExpiryTime: Int let firstLogin: Bool let externalToken: String let zat: String let zrt: String let player: Player } struct Player: Content { let idpId: String let appId: String let playerId: String let pushOption: PushOptionResponse let regTime: Int let idpAlias: String let firstLoginTime: Int let status: String } struct AgreementResponse: Content { let E001: String let E002: String let E006: String let N002: String let N003: String let timestamp: String } struct PushOptionResponse: Content { let night: String let player: String }