implement /infodesk endpoints

This commit is contained in:
Andrew Glaze
2025-05-15 18:29:54 -04:00
parent a94752a799
commit 04c5660f7d
8 changed files with 686 additions and 41 deletions

View File

@@ -0,0 +1,188 @@
import Fluent
import Vapor
struct InfodeskController: RouteCollection {
func boot(routes: any RoutesBuilder) throws {
let group = routes.grouped("infodesk", "v2")
group.get("appGroup", use: self.appGroup)
group.get("app", use: self.app)
}
@Sendable
func appGroup(req: Request) async throws -> Response {
var headers = HTTPHeaders()
headers.add(name: "sig", value: "0;F8rysQxii/VL6Rca6Gnw/lq1AXA0N1RfAkKHosaiYWM=")
return Response(status: .ok, headers: headers, body: .init(string: """
{
"status": 200,
"desc": "OK",
"content": {
"timestamp": 1717794472083,
"apps": [
{
"notices": [],
"appId": "535877",
"dataMap": {
"countryCodes": "kr",
"countryCodeList": "kr",
"displayName": "\u{c6d4}\u{b4dc} \u{d50c}\u{b9ac}\u{d37c}",
"forceServerSelectDeviceList": "iPad6,4 iPad7,2 iPad7,4 iPad13,6",
"bannedCountryCodeList": "-",
"isServiceAvailable": "true"
}
},
{
"notices": [],
"appId": "561429",
"dataMap": {
"countryCodes": "us",
"countryCodeList": "ag,ai,an,ar,aw,bb,bl,bm,bo,bq,br,bs,bz,ca,cl,co,cr,cu,cw,dm,do,ec,fk,gd,gf,gp,gs,gt,gy,hn,ht,jm,kn,ky,lc,mf,mq,ms,mx,ni,pa,pe,pm,pr,py,sr,sv,sx,tc,tt,um,us,uy,vc,ve,vg,vi",
"displayName": "World Flipper (NA)",
"forceServerSelectDeviceList": "iPad6,4 iPad7,2 iPad7,4 iPad13,6",
"bannedCountryCodeList": "-",
"isServiceAvailable": "true"
}
},
{
"notices": [],
"appId": "561430",
"dataMap": {
"countryCodes": "de",
"countryCodeList": "ad,ae,al,am,ao,at,ax,az,ba,be,bf,bg,bh,bi,bj,bv,bw,by,cd,cf,cg,ch,ci,cm,cv,cy,cz,de,dj,dk,dz,ee,eg,eh,er,es,et,fi,fo,fr,ga,gb,ge,gg,gh,gi,gl,gm,gn,gq,gr,gw,hr,hu,ie,il,im,iq,ir,is,it,je,jo,ke,km,kw,lb,li,lr,ls,lt,lu,lv,ly,ma,mc,md,me,mg,mk,ml,mr,mt,mu,mw,mz,na,ne,ng,nl,no,om,pl,ps,pt,qa,re,ro,rs,ru,rw,sa,sc,sd,se,sh,si,sj,sk,sl,sm,sn,so,ss,st,sy,sz,td,tf,tg,tn,tr,tz,ua,ug,uz,va,ye,yt,za,zm,zw",
"displayName": "World Flipper (EU)",
"forceServerSelectDeviceList": "iPad6,4 iPad7,2 iPad7,4 iPad13,6",
"bannedCountryCodeList": "-",
"isServiceAvailable": "true"
}
},
{
"notices": [],
"appId": "561432",
"dataMap": {
"countryCodes": "th",
"countryCodeList": "af,as,au,bd,bn,bt,cc,ck,cx,fj,fm,gu,hm,id,in,io,kg,kh,ki,kz,la,lk,mh,mm,mn,mp,mv,my,nc,nf,np,nr,nu,nz,pf,pg,ph,pk,pn,pw,sb,sg,th,tj,tk,tl,tm,to,tv,vn,vu,wf,ws",
"displayName": "World Flipper (SEA)",
"forceServerSelectDeviceList": "iPad6,4 iPad7,2 iPad7,4 iPad13,6",
"bannedCountryCodeList": "-",
"isServiceAvailable": "true"
}
}
]
}
}
"""))
}
@Sendable
func app(req: Request) async throws -> Response {
var headers = HTTPHeaders()
headers.add(name: "sig", value: "0;F8rysQxii/VL6Rca6Gnw/lq1AXA0N1RfAkKHosaiYWM=")
return Response(status: .ok, headers: headers, body: .init(string: #"""
{
"status": 200,
"desc": "OK",
"content": {
"supportedFeatures": [
"urgentNotice",
"maintenance",
"push",
"delivery",
"promotion",
"coupon",
"notice"
],
"marketUrl": "market://details?id=com.kakaogames.wdfp",
"publicKeyMap": {},
"secondaryPwOption": null,
"capriAppOption": {
"ageLimit": 0,
"lazyAgeAuth": null,
"appType": "LEGACY_PARTNER",
"appCategory": "Games",
"ageAuthLevel": "NONE"
},
"isTubeApp": false,
"verRecent": "0.0.81",
"appOption": {
"urlCommunity": "https://twitter.com/Worldflipper_kg",
"urlOtherMenuOfficialCafe": "https://twitter.com/Worldflipper_kg",
"urlTitleMenuContact": "oqupie",
"cdnAddr": "http://patch.wdfp.kakaogames.com/Live/2.0.0",
"agreementUrl": "https://web-data-game.kakaocdn.net/real/www/html/agreement/index.html?tid=13",
"useCoupon": "true",
"useGoogleGame": "FALSE",
"urlPrivacyPolicy": "https://web-data-cdn.kakaogames.com/real/www/html/terms/index.html?service=S0001&type=T003",
"urlFriendFollowServer": "https://na.wdfp.kakaogames.com",
"useHttpHeartbeat": "true",
"isReproduceS3UploadOpen": "false",
"urlTermsAndConditions": "https://web-data-game.kakaocdn.net/real/www/html/terms/index.html?service=S0001&type=T001&country=us&lang=en",
"urlHomeNews": "https://worldflipper.playkakaogames.com/news",
"gameServerAddr": "https://na.wdfp.kakaogames.com",
"modTime": 1617070960617,
"urlTitleMenuNews": "https://worldflipper.playkakaogames.com/news",
"refreshInfodeskIntervalMin": "5",
"urlOtherMenuContact": "oqupie",
"urlNotice": "https://worldflipper.playkakaogames.com/news",
"urlReviewContact": "https://kakaogames.oqupie.com/portals/2060"
},
"notices": [],
"traceSampleRate": 0,
"isWhitelist": false,
"svcStatus": "open",
"supportedIdpCodes": [
"facebook",
"google",
"siwa",
"zd3"
],
"serverConnectionType": "https",
"appVerStatus": "noNeedToUpdate",
"publisher": {
"privacyUrl": "https://www.kakao.com/ko/privacy",
"privacySummaryUrl": "https://gameevent.kakao.com/supports/terms/3?tabbar=false",
"noticeUrl2": "https://cus-zinny3.kakaogames.com/view/notice",
"agreementUrl": "https://web-data-game.kakaocdn.net/real/www/html/agreement/index.html?tid=13",
"servicePolicyUrl": "https://gameevent.kakao.com/terms/operation",
"termsUrl": "https://gameevent.kakao.com/supports/terms/1",
"kakaogameCommunityUrl": "https://playgame.kakao.com/bridge/auth/zinny",
"termsSummaryUrl": "https://gameevent.kakao.com/supports/terms/1?tabbar=false",
"eventWallUrl": "https://cus-zinny3.kakaogames.com/view/event",
"noticeUrl": "https://cus-zinny3.kakaogames.com/notice",
"customerServiceUrl": "https://cus-zinny3.kakaogames.com/support/list",
"eventWinnerUrl": "http://event-winner.kakaogames.com/event",
"policyVer": "1.0",
"publisherId": "kakao",
"modTime": 1651813742832
},
"sdk": {
"heartbeatInterval": 120000,
"PercentOfSendingAPICallLog": 0,
"stopSendGeoDNS": "y",
"snsShareUrl": "https://invite.kakaogame.com",
"zrtiOSError": "{\"kakaocapri\":[500, 502, 503, -1, -7, -9]}",
"aesEncryptKey": "djfdskj12328438djdgjcjeejhdew15",
"aesEncryptIV": "7gnfn7f96rnanmt1s5iaa3kdruhuneu",
"cafeLoginUrl": "https://accounts.kakao.com/weblogin/sso_login?token={tgt_token}&token_type=tgt&continue={url}",
"zrtAOSError": "{\"kakaocapri\":[500, 502, 503, -1, -7, -9],\"google\":[8]}",
"zrtWindowsError": "{\"kakaocapri\":[500, 502, 503, -1, -7, -9]}",
"snsShareHostUrl": "https://invite.kakaogame.com/host/main",
"invitationUrl": "https://webinvite.nzincorp.com",
"csUrl": "http://customer.kakaogames.com:18080",
"platformVersion": 3,
"sessionTimeout": 10000,
"registerDeviceUrl": "https://device-enrollment.kakaogames.com/main",
"customDialogModels": [
"SM-T976N"
],
"unregisterAgreementUrl": "https://web-data-cdn.kakaogames.com/real/www/html/terms/index.html?service=S0001&type=T016",
"snsShareGuestUrl": "https://invite.kakaogame.com/guest/reward"
},
"deviceSecurityOption": null,
"onlineNotifications": [],
"timestamp": 1717794472611
}
}
"""#))
}
}

View File

@@ -1,37 +0,0 @@
import Fluent
import Vapor
struct TodoController: RouteCollection {
func boot(routes: any RoutesBuilder) throws {
let todos = routes.grouped("todos")
todos.get(use: self.index)
todos.post(use: self.create)
todos.group(":todoID") { todo in
todo.delete(use: self.delete)
}
}
@Sendable
func index(req: Request) async throws -> [TodoDTO] {
try await Todo.query(on: req.db).all().map { $0.toDTO() }
}
@Sendable
func create(req: Request) async throws -> TodoDTO {
let todo = try req.content.decode(TodoDTO.self).toModel()
try await todo.save(on: req.db)
return todo.toDTO()
}
@Sendable
func delete(req: Request) async throws -> HTTPStatus {
guard let todo = try await Todo.find(req.parameters.get("todoID"), on: req.db) else {
throw Abort(.notFound)
}
try await todo.delete(on: req.db)
return .noContent
}
}

View File

@@ -8,9 +8,11 @@ public func configure(_ app: Application) async throws {
// uncomment to serve files from /Public folder
// app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
app.databases.use(DatabaseConfigurationFactory.sqlite(.file("db.sqlite")), as: .sqlite)
app.databases.use(DatabaseConfigurationFactory.sqlite(.file("db.sqlite")), as: .sqlite)
app.migrations.add(CreateTodo())
app.http.server.configuration.hostname = "0.0.0.0"
app.http.server.configuration.port = 8000
// register routes
try routes(app)

View File

@@ -0,0 +1,140 @@
import Vapor
struct OpenApi {
func registerRoutes(_ app: Application) {
let group = app.grouped("openapi", "service")
group.post("v3", "util", "country", "get") { req async -> String in
"{\"country\": \"us\"}"
}
group.post("v4", "device", "accessToken", "create") { req async -> AccessTokenResponse in
AccessTokenResponse(accessToken: "fwPla7fQ8ty9+DZT/lD//uWZD4uD6C4lD6gGIIZTLKRTQ52/SLCRmk/370jcWGs+e+1iSoZtL7lj8ov9B0/jHmijH4nsHPQT6pchaQM1M9mtwYNQq0BWhVr9hF0jjCK/a5LIVd1kBac/Gemv29WKEDKSrUS9HxxUigoPRwtOy8m+oDj9FmDJZ+rzqWCc0QjES4Ky0fTpXZ7ESoguDzNmRtW3FYr+OFexw8wBPlwiC4w=", expiryTime: Int(Date.init(timeIntervalSinceNow: .init(0)).timeIntervalSince1970))
}
group.post("v3", "agreement", "getForLogin") { req async throws -> String in
let body = try req.content.decode(LoginAgreementRequest.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
}
"""#
}
group.post("v4", "auth", "loginDevice") { req async throws -> Response in
let body = try req.content.decode(LoginDeviceRequest.self, as: .json)
dump(body)
if let rawAccountId = req.headers["playerId"].first {
}
return Response(status: .notImplemented)
}
}
}
struct AccessTokenResponse: Content {
let accessToken: String
let expiryTime: Int
}
struct LoginAgreementRequest: 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 LoginDeviceRequest: 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 LoginDeviceResponse: 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 lang: String
let playerId: String
let agreement: AgreementResponse
let pushOption: PushOptionResponse
let lastLoginTime: Int
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
}

View File

@@ -2,7 +2,7 @@ import Fluent
import Vapor
func routes(_ app: Application) throws {
app.get { req async in
app.get { req async in
"It works!"
}
@@ -10,5 +10,10 @@ app.get { req async in
"Hello, world!"
}
try app.register(collection: TodoController())
let openApi = OpenApi()
openApi.registerRoutes(app)
try app.register(collection: InfodeskController())
print(app.routes.all)
}