diff --git a/.gitignore b/.gitignore index 90a6d3f..c55ffc0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,7 @@ db.sqlite .swiftpm .env .env.* -!.env.example \ No newline at end of file +!.env.example + +cdn/ +__pycache__/ diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..6f57d35 --- /dev/null +++ b/Package.resolved @@ -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 +} diff --git a/Sources/stella/Controllers/InfodeskController.swift b/Sources/stella/Controllers/InfodeskController.swift new file mode 100644 index 0000000..2129ff9 --- /dev/null +++ b/Sources/stella/Controllers/InfodeskController.swift @@ -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 + } + } + """#)) + } +} diff --git a/Sources/stella/Controllers/TodoController.swift b/Sources/stella/Controllers/TodoController.swift deleted file mode 100644 index e1e9204..0000000 --- a/Sources/stella/Controllers/TodoController.swift +++ /dev/null @@ -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 - } -} diff --git a/Sources/stella/configure.swift b/Sources/stella/configure.swift index 80334f2..4da269c 100644 --- a/Sources/stella/configure.swift +++ b/Sources/stella/configure.swift @@ -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) diff --git a/Sources/stella/routes/openapi.swift b/Sources/stella/routes/openapi.swift new file mode 100644 index 0000000..4edef9f --- /dev/null +++ b/Sources/stella/routes/openapi.swift @@ -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 +} diff --git a/Sources/stella/routes.swift b/Sources/stella/routes/routes.swift similarity index 50% rename from Sources/stella/routes.swift rename to Sources/stella/routes/routes.swift index 4f0a094..0ab5799 100644 --- a/Sources/stella/routes.swift +++ b/Sources/stella/routes/routes.swift @@ -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) } diff --git a/mitm-redirect-traffic.py b/mitm-redirect-traffic.py new file mode 100644 index 0000000..3d5cb22 --- /dev/null +++ b/mitm-redirect-traffic.py @@ -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}"