mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 05:43:41 +03:00
Multi platform support
This commit is contained in:
@@ -18,6 +18,26 @@ var loginFS embed.FS
|
||||
|
||||
var loginTmpl = template.Must(template.ParseFS(loginFS, "login.html"))
|
||||
|
||||
// oauthRedirectTmpl drives the fbconnect:// hand-off via a renderer-initiated
|
||||
// navigation instead of a server-side 302. Android WebView does NOT invoke
|
||||
// WebViewClient.shouldOverrideUrlLoading for 302 redirects from POST form
|
||||
// submissions to non-http schemes (documented Chromium WebView limitation,
|
||||
// Stack Overflow #6738328 / Google issuetracker #36918490). Returning a 200
|
||||
// HTML page with both <meta http-equiv="refresh"> and window.location.replace()
|
||||
// makes the cross-scheme navigation renderer-initiated, which DOES invoke
|
||||
// shouldOverrideUrlLoading, so the FB SDK can extract access_token from the
|
||||
// URL fragment and complete its login flow. html/template auto-escapes {{.}}
|
||||
// correctly for the meta URL-attribute context and the JS string-literal
|
||||
// context inside <script>.
|
||||
var oauthRedirectTmpl = template.Must(template.New("oauthRedirect").Parse(
|
||||
`<!doctype html><html><head><meta charset="utf-8">
|
||||
<meta http-equiv="refresh" content="0;url={{.}}">
|
||||
<script>window.location.replace({{.}});</script>
|
||||
</head><body>
|
||||
<noscript><a href="{{.}}">Continue</a></noscript>
|
||||
</body></html>
|
||||
`))
|
||||
|
||||
type Handlers struct {
|
||||
store *AuthStore
|
||||
tok *TokenService
|
||||
@@ -30,6 +50,7 @@ func NewHandlers(store *AuthStore, tok *TokenService) *Handlers {
|
||||
type loginPageData struct {
|
||||
RedirectURI string
|
||||
State string
|
||||
Scope string
|
||||
Error string
|
||||
Username string
|
||||
}
|
||||
@@ -77,6 +98,7 @@ func (h *Handlers) oauthGet(w http.ResponseWriter, r *http.Request) {
|
||||
data := loginPageData{
|
||||
RedirectURI: r.URL.Query().Get("redirect_uri"),
|
||||
State: r.URL.Query().Get("state"),
|
||||
Scope: r.URL.Query().Get("scope"),
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := loginTmpl.Execute(w, data); err != nil {
|
||||
@@ -95,11 +117,13 @@ func (h *Handlers) oauthPost(w http.ResponseWriter, r *http.Request) {
|
||||
action := r.FormValue("action")
|
||||
redirectURI := r.FormValue("redirect_uri")
|
||||
state := r.FormValue("state")
|
||||
scope := r.FormValue("scope")
|
||||
|
||||
renderErr := func(msg string) {
|
||||
data := loginPageData{
|
||||
RedirectURI: redirectURI,
|
||||
State: state,
|
||||
Scope: scope,
|
||||
Error: msg,
|
||||
Username: username,
|
||||
}
|
||||
@@ -158,20 +182,31 @@ func (h *Handlers) oauthPost(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
payload := fmt.Sprintf(`{"user_id":"%d"}`, user.ID)
|
||||
b64 := base64.StdEncoding.EncodeToString([]byte(payload))
|
||||
b64 := base64.RawURLEncoding.EncodeToString([]byte(payload))
|
||||
|
||||
fragment := url.Values{}
|
||||
fragment.Set("access_token", token)
|
||||
fragment.Set("token_type", "bearer")
|
||||
fragment.Set("expires_in", strconv.FormatInt(int64(tokenTTL.Seconds()), 10))
|
||||
fragment.Set("signed_request", "0."+b64)
|
||||
// iOS FBSDKLoginManager treats an empty granted_scopes set as a cancelled login
|
||||
// (LoginManager.swift -> getSuccessResult -> getCancelledResult). Echo back the
|
||||
// scope the SDK sent so parameters.permissions is non-empty and the SDK fires
|
||||
// its success path. Android tolerates either way.
|
||||
if scope != "" {
|
||||
fragment.Set("granted_scopes", scope)
|
||||
fragment.Set("denied_scopes", "")
|
||||
}
|
||||
if state != "" {
|
||||
fragment.Set("state", state)
|
||||
}
|
||||
|
||||
target := redirectURI + "?" + fragment.Encode()
|
||||
log.Printf("redirecting to %s", target)
|
||||
http.Redirect(w, r, target, http.StatusFound)
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := oauthRedirectTmpl.Execute(w, target); err != nil {
|
||||
log.Printf("render oauth redirect: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handlers) HandleCheckUsername(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -128,6 +128,7 @@
|
||||
|
||||
<input type="hidden" name="redirect_uri" value="{{.RedirectURI}}">
|
||||
<input type="hidden" name="state" value="{{.State}}">
|
||||
<input type="hidden" name="scope" value="{{.Scope}}">
|
||||
|
||||
<label for="username">Username</label>
|
||||
<input type="text" id="username" name="username" value="{{.Username}}" autocomplete="username" autofocus required>
|
||||
|
||||
+52
-11
@@ -122,23 +122,62 @@ type assetCheck struct {
|
||||
var requiredAssets = []assetCheck{
|
||||
{"assets", true},
|
||||
{"assets/release/20240404193219.bin.e", false},
|
||||
{"assets/revisions/0/list.bin", false},
|
||||
{"assets/revisions/0/assetbundle", true},
|
||||
{"assets/revisions/0/resources", true},
|
||||
}
|
||||
|
||||
// platformAssets holds the per-platform rev-0 layout. At least one full set
|
||||
// must be present; users may have only Android or only iOS extracted.
|
||||
var platformAssets = [][]assetCheck{
|
||||
{
|
||||
{"assets/revisions/0/android/list.bin", false},
|
||||
{"assets/revisions/0/android/assetbundle", true},
|
||||
{"assets/revisions/0/android/resources", true},
|
||||
},
|
||||
{
|
||||
{"assets/revisions/0/ios/list.bin", false},
|
||||
{"assets/revisions/0/ios/assetbundle", true},
|
||||
{"assets/revisions/0/ios/resources", true},
|
||||
},
|
||||
}
|
||||
|
||||
func checkAsset(a assetCheck) (missing string, ok bool) {
|
||||
info, err := os.Stat(a.path)
|
||||
if err != nil {
|
||||
return a.path, false
|
||||
}
|
||||
if a.dir && !info.IsDir() {
|
||||
return a.path + string(filepath.Separator), false
|
||||
}
|
||||
if !a.dir && info.IsDir() {
|
||||
return a.path, false
|
||||
}
|
||||
return "", true
|
||||
}
|
||||
|
||||
func validateAssets() {
|
||||
var missing []string
|
||||
for _, a := range requiredAssets {
|
||||
info, err := os.Stat(a.path)
|
||||
if err != nil {
|
||||
missing = append(missing, a.path)
|
||||
continue
|
||||
if m, ok := checkAsset(a); !ok {
|
||||
missing = append(missing, m)
|
||||
}
|
||||
if a.dir && !info.IsDir() {
|
||||
missing = append(missing, a.path+string(filepath.Separator))
|
||||
} else if !a.dir && info.IsDir() {
|
||||
missing = append(missing, a.path)
|
||||
}
|
||||
|
||||
var platformMissing [][]string
|
||||
anyPlatformOK := false
|
||||
for _, group := range platformAssets {
|
||||
var groupMissing []string
|
||||
for _, a := range group {
|
||||
if m, ok := checkAsset(a); !ok {
|
||||
groupMissing = append(groupMissing, m)
|
||||
}
|
||||
}
|
||||
if len(groupMissing) == 0 {
|
||||
anyPlatformOK = true
|
||||
}
|
||||
platformMissing = append(platformMissing, groupMissing)
|
||||
}
|
||||
if !anyPlatformOK {
|
||||
for _, gm := range platformMissing {
|
||||
missing = append(missing, gm...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +200,8 @@ func validateAssets() {
|
||||
b.WriteString("\n")
|
||||
b.WriteString(dimStyle.Render(" Place the extracted game assets under server/assets/ and try again."))
|
||||
b.WriteString("\n")
|
||||
b.WriteString(dimStyle.Render(" At least one of assets/revisions/0/android/ or assets/revisions/0/ios/ must be fully present."))
|
||||
b.WriteString("\n")
|
||||
b.WriteString(dimStyle.Render(" Get them from ") + hlStyle.Render("#resources") + dimStyle.Render(" on Discord: ") + hlStyle.Hyperlink("https://discord.com/invite/MZAf5aVkJG").Render("https://discord.com/invite/MZAf5aVkJG"))
|
||||
b.WriteString("\n")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user