implement /infodesk endpoints
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -8,4 +8,7 @@ db.sqlite
|
||||
.swiftpm
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.example
|
||||
|
||||
cdn/
|
||||
__pycache__/
|
||||
|
285
Package.resolved
Normal file
285
Package.resolved
Normal file
@@ -0,0 +1,285 @@
|
||||
{
|
||||
"originHash" : "d53dfe2770a8b5d4063e71737e72637f59cec754bf4553178a66d10f39bfb1ee",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "async-http-client",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swift-server/async-http-client.git",
|
||||
"state" : {
|
||||
"revision" : "3b265e6a00fc5c3fdb8f91f773e506990c704337",
|
||||
"version" : "1.26.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "async-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/async-kit.git",
|
||||
"state" : {
|
||||
"revision" : "e048c8ee94967e8d8a1c2ec0e1156d6f7fa34d31",
|
||||
"version" : "1.20.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "console-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/console-kit.git",
|
||||
"state" : {
|
||||
"revision" : "742f624a998cba2a9e653d9b1e91ad3f3a5dff6b",
|
||||
"version" : "4.15.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "fluent",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/fluent.git",
|
||||
"state" : {
|
||||
"revision" : "223b27d04ab2b51c25503c9922eecbcdf6c12f89",
|
||||
"version" : "4.12.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "fluent-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/fluent-kit.git",
|
||||
"state" : {
|
||||
"revision" : "8baacd7e8f7ebf68886c496b43bbe6cdcc5b57e0",
|
||||
"version" : "1.52.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "fluent-sqlite-driver",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/fluent-sqlite-driver.git",
|
||||
"state" : {
|
||||
"revision" : "73529a63ab11c7fe87da17b5a67a1b1f58c020f8",
|
||||
"version" : "4.8.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "multipart-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/multipart-kit.git",
|
||||
"state" : {
|
||||
"revision" : "3498e60218e6003894ff95192d756e238c01f44e",
|
||||
"version" : "4.7.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "routing-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/routing-kit.git",
|
||||
"state" : {
|
||||
"revision" : "93f7222c8e195cbad39fafb5a0e4cc85a8def7ea",
|
||||
"version" : "4.9.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "sql-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/sql-kit.git",
|
||||
"state" : {
|
||||
"revision" : "baf0d8684a43f16cd11ebcc67300c8ab5cb5d078",
|
||||
"version" : "3.33.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "sqlite-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/sqlite-kit.git",
|
||||
"state" : {
|
||||
"revision" : "f35a863ecc2da5d563b836a9a696b148b0f4169f",
|
||||
"version" : "4.5.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "sqlite-nio",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/sqlite-nio.git",
|
||||
"state" : {
|
||||
"revision" : "fe1257499440de396504eea2b7793ce1f984c324",
|
||||
"version" : "1.11.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-algorithms",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-algorithms.git",
|
||||
"state" : {
|
||||
"revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023",
|
||||
"version" : "1.2.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-asn1",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-asn1.git",
|
||||
"state" : {
|
||||
"revision" : "a54383ada6cecde007d374f58f864e29370ba5c3",
|
||||
"version" : "1.3.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-atomics",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-atomics.git",
|
||||
"state" : {
|
||||
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
|
||||
"version" : "1.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-collections",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-collections.git",
|
||||
"state" : {
|
||||
"revision" : "671108c96644956dddcd89dd59c203dcdb36cec7",
|
||||
"version" : "1.1.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-crypto",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-crypto.git",
|
||||
"state" : {
|
||||
"revision" : "e8d6eba1fef23ae5b359c46b03f7d94be2f41fed",
|
||||
"version" : "3.12.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-distributed-tracing",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-distributed-tracing.git",
|
||||
"state" : {
|
||||
"revision" : "a64a0abc2530f767af15dd88dda7f64d5f1ff9de",
|
||||
"version" : "1.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-http-structured-headers",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-http-structured-headers.git",
|
||||
"state" : {
|
||||
"revision" : "db6eea3692638a65e2124990155cd220c2915903",
|
||||
"version" : "1.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-http-types",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-http-types.git",
|
||||
"state" : {
|
||||
"revision" : "a0a57e949a8903563aba4615869310c0ebf14c03",
|
||||
"version" : "1.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-log",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-log.git",
|
||||
"state" : {
|
||||
"revision" : "3d8596ed08bd13520157f0355e35caed215ffbfa",
|
||||
"version" : "1.6.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-metrics",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-metrics.git",
|
||||
"state" : {
|
||||
"revision" : "4c83e1cdf4ba538ef6e43a9bbd0bcc33a0ca46e3",
|
||||
"version" : "2.7.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio.git",
|
||||
"state" : {
|
||||
"revision" : "34d486b01cd891297ac615e40d5999536a1e138d",
|
||||
"version" : "2.83.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio-extras",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-extras.git",
|
||||
"state" : {
|
||||
"revision" : "f1f6f772198bee35d99dd145f1513d8581a54f2c",
|
||||
"version" : "1.26.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio-http2",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-http2.git",
|
||||
"state" : {
|
||||
"revision" : "4281466512f63d1bd530e33f4aa6993ee7864be0",
|
||||
"version" : "1.36.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio-ssl",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-ssl.git",
|
||||
"state" : {
|
||||
"revision" : "4b38f35946d00d8f6176fe58f96d83aba64b36c7",
|
||||
"version" : "2.31.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio-transport-services",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-transport-services.git",
|
||||
"state" : {
|
||||
"revision" : "cd1e89816d345d2523b11c55654570acd5cd4c56",
|
||||
"version" : "1.24.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-numerics",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-numerics.git",
|
||||
"state" : {
|
||||
"revision" : "e0ec0f5f3af6f3e4d5e7a19d2af26b481acb6ba8",
|
||||
"version" : "1.0.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-service-context",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-service-context.git",
|
||||
"state" : {
|
||||
"revision" : "8946c930cae601452149e45d31d8ddfac973c3c7",
|
||||
"version" : "1.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-system",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-system.git",
|
||||
"state" : {
|
||||
"revision" : "a34201439c74b53f0fd71ef11741af7e7caf01e1",
|
||||
"version" : "1.4.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "vapor",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/vapor.git",
|
||||
"state" : {
|
||||
"revision" : "87b0edd2633c35de543cb7573efe5fbf456181bc",
|
||||
"version" : "4.114.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "websocket-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/websocket-kit.git",
|
||||
"state" : {
|
||||
"revision" : "8666c92dbbb3c8eefc8008c9c8dcf50bfd302167",
|
||||
"version" : "2.16.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
188
Sources/stella/Controllers/InfodeskController.swift
Normal file
188
Sources/stella/Controllers/InfodeskController.swift
Normal 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
|
||||
}
|
||||
}
|
||||
"""#))
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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)
|
||||
|
140
Sources/stella/routes/openapi.swift
Normal file
140
Sources/stella/routes/openapi.swift
Normal 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
|
||||
}
|
@@ -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)
|
||||
}
|
59
mitm-redirect-traffic.py
Normal file
59
mitm-redirect-traffic.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from mitmproxy import http, dns
|
||||
import ipaddress
|
||||
import logging
|
||||
|
||||
API_HOST = "localhost"
|
||||
# 198.51.100.0/24 subnet reserved for documentation, so it will never be used for anything else
|
||||
#We can black-hole this address because it will be discarded.
|
||||
API_DNS_REDIRECT_HOST = ipaddress.IPv4Address("198.51.100.140")
|
||||
DNS_TTL= 600
|
||||
API_PORT = 8000
|
||||
API_SCHEME = 'http'
|
||||
MAGIC_DOMAIN_SUFFIX = ".mitm.it"
|
||||
|
||||
prefixes = ["/openapi", "/infodesk", "", '/patch']
|
||||
|
||||
# hostname: prefix_index
|
||||
hosts = {
|
||||
# openapi
|
||||
"openapi-zinny3.game.kakao.com": 0,
|
||||
"gc-openapi-zinny3.kakaogames.com": 0,
|
||||
|
||||
# infodesk
|
||||
"gc-infodesk-zinny3.kakaogames.com": 1,
|
||||
|
||||
# na server
|
||||
"na.wdfp.kakaogames.com": 2,
|
||||
|
||||
# patch
|
||||
"patch.wdfp.kakaogames.com": 3
|
||||
}
|
||||
|
||||
def dns_request(flow: dns.DNSFlow):
|
||||
if not flow.request.query or flow.request.questions is None: return
|
||||
logging.info(f"[INFO] DNS request for {flow.request.questions}")
|
||||
for question in flow.request.questions:
|
||||
name = question.name
|
||||
prefix_type = hosts.get(name) if question.type == 1 else None
|
||||
if prefix_type != None: #Type is 1 when asking for an A record
|
||||
#logging.info(f"[INFO] Matched DNS request for {question.name}")
|
||||
flow.response.answers = [answer for answer in flow.response.answers if answer.name != question.name]
|
||||
domain_redirect = f'{question.name}{MAGIC_DOMAIN_SUFFIX}'
|
||||
#TODO: Are the CNAME records still useful in this configuration? Might be better to remove them and the host_redirects altogether....
|
||||
cname_rec = dns.ResourceRecord.CNAME(question.name, domain_redirect, ttl=DNS_TTL)
|
||||
a_rec = dns.ResourceRecord.A(domain_redirect, API_DNS_REDIRECT_HOST, ttl=DNS_TTL)
|
||||
flow.response.answers.append(cname_rec)
|
||||
flow.response.answers.append(a_rec)
|
||||
logging.info(f"[INFO] Answered with {flow.response.answers}")
|
||||
|
||||
def request(flow: http.HTTPFlow):
|
||||
logging.info(f"[INFO] {flow.request.url}")
|
||||
prefix_type = hosts.get(flow.request.pretty_host)
|
||||
if prefix_type != None:
|
||||
flow.request.host = API_HOST
|
||||
flow.request.port = API_PORT
|
||||
flow.request.scheme = API_SCHEME
|
||||
|
||||
prefix = prefixes[prefix_type]
|
||||
if prefix != "":
|
||||
flow.request.path = f"{prefix}{flow.request.path}"
|
Reference in New Issue
Block a user