Move login call to native
parent
dbfd56e727
commit
49f97e2895
|
@ -41,17 +41,6 @@ div.p {
|
|||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
p.error {
|
||||
display: none;
|
||||
color: red;
|
||||
font-weight: 500;
|
||||
word-wrap: break-word;
|
||||
user-select: text;
|
||||
background: white;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
color: var(--fg-color);
|
||||
margin-bottom: 30px;
|
||||
|
@ -106,22 +95,3 @@ input.m-input:focus {
|
|||
margin: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#folder-list {
|
||||
margin: 15px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.folder-choose {
|
||||
font-size: 1.1em;
|
||||
margin-top: 4px;
|
||||
display: grid;
|
||||
grid-template-columns: 1em auto;
|
||||
gap: 1.2em;
|
||||
line-height: 1.8em;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
}
|
||||
|
|
|
@ -26,8 +26,6 @@
|
|||
</button>
|
||||
<br />
|
||||
|
||||
<p id="conn-error" class="error"></p>
|
||||
|
||||
<a class="m-button link" href="https://memories.gallery/install/">
|
||||
I don't have a server
|
||||
</a>
|
||||
|
@ -36,7 +34,6 @@
|
|||
<script>
|
||||
const urlBox = document.getElementById("server-url");
|
||||
const loginButton = document.getElementById("login");
|
||||
const connError = document.getElementById("conn-error");
|
||||
|
||||
function validateUrl(url) {
|
||||
try {
|
||||
|
@ -86,10 +83,6 @@
|
|||
|
||||
// Login button click handler
|
||||
loginButton.addEventListener("click", async () => {
|
||||
const url = getMemoriesUrl();
|
||||
url.pathname += "api/describe";
|
||||
const urlStr = url.toString();
|
||||
|
||||
try {
|
||||
urlBox.disabled = true;
|
||||
loginButton.disabled = true;
|
||||
|
@ -98,20 +91,17 @@
|
|||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
||||
|
||||
// Get API description
|
||||
const res = await fetch(urlStr, {
|
||||
// Login signal
|
||||
const encUrl = encodeURIComponent(encodeURIComponent(getMemoriesUrl().toString()));
|
||||
await fetch(`http://127.0.0.1/api/login/${encUrl}`, {
|
||||
method: "GET",
|
||||
signal: controller.signal,
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
// API is fine, redirect to login page
|
||||
clearTimeout(timeoutId);
|
||||
window.nativex.login(data.baseUrl, data.loginFlowUrl);
|
||||
} catch (e) {
|
||||
// CORS request, we know nothing about the server
|
||||
connError.innerText = `Failed to fetch server info.\nIs Memories installed and enabled?\n${urlStr}`;
|
||||
connError.style.display = "block";
|
||||
// unreachable?
|
||||
} finally {
|
||||
urlBox.disabled = false;
|
||||
loginButton.disabled = false;
|
||||
|
|
|
@ -43,6 +43,8 @@ class NativeX(private val mCtx: MainActivity) {
|
|||
}
|
||||
|
||||
object API {
|
||||
val LOGIN = Regex("^/api/login/.+$")
|
||||
|
||||
val DAYS = Regex("^/api/days$")
|
||||
val DAY = Regex("^/api/days/\\d+$")
|
||||
|
||||
|
@ -93,12 +95,6 @@ class NativeX(private val mCtx: MainActivity) {
|
|||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun login(baseUrl: String?, loginFlowUrl: String?) {
|
||||
if (baseUrl == null || loginFlowUrl == null) return;
|
||||
account.login(baseUrl, loginFlowUrl)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun logout() {
|
||||
account.loggedOut()
|
||||
|
@ -227,7 +223,9 @@ class NativeX(private val mCtx: MainActivity) {
|
|||
val path = request.url.path ?: return makeErrorResponse()
|
||||
|
||||
val parts = path.split("/").toTypedArray()
|
||||
return if (path.matches(API.DAYS)) {
|
||||
return if (path.matches(API.LOGIN)) {
|
||||
makeResponse(account.login(URLDecoder.decode(parts[3], "UTF-8")))
|
||||
} else if (path.matches(API.DAYS)) {
|
||||
makeResponse(query.getDays())
|
||||
} else if (path.matches(API.DAY)) {
|
||||
makeResponse(query.getDay(parts[3].toLong()))
|
||||
|
|
|
@ -9,7 +9,6 @@ import androidx.media3.common.util.UnstableApi
|
|||
import gallery.memories.MainActivity
|
||||
import gallery.memories.R
|
||||
import io.github.g00fy2.versioncompare.Version
|
||||
import java.net.SocketTimeoutException
|
||||
|
||||
@UnstableApi
|
||||
class AccountService(private val mCtx: MainActivity, private val mHttp: HttpService) {
|
||||
|
@ -19,42 +18,59 @@ class AccountService(private val mCtx: MainActivity, private val mHttp: HttpServ
|
|||
|
||||
private val store = SecureStorage(mCtx)
|
||||
|
||||
/**
|
||||
* Make the first request to log in
|
||||
* @param url The URL of the Nextcloud server
|
||||
*/
|
||||
fun login(url: String) {
|
||||
try {
|
||||
Log.v(TAG, "login: Connecting to ${url}api/describe")
|
||||
mHttp.setBaseUrl(url)
|
||||
val res = mHttp.getApiDescription()
|
||||
|
||||
if (res.code != 200) {
|
||||
throw Exception("${url}api/describe (status ${res.code})")
|
||||
}
|
||||
|
||||
val body = mHttp.bodyJson(res) ?: throw Exception("Failed to parse API description")
|
||||
|
||||
val baseUrl = body.getString("baseUrl")
|
||||
val loginFlowUrl = body.getString("loginFlowUrl")
|
||||
loginFlow(baseUrl, loginFlowUrl)
|
||||
} catch (e: Exception) {
|
||||
toast("Error: ${e.message}")
|
||||
throw Exception("Failed to connect to server: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Login to a server
|
||||
* @param baseUrl The base URL of the server
|
||||
* @param loginFlowUrl The login flow URL
|
||||
* @throws Exception If the login flow failed
|
||||
*/
|
||||
fun login(baseUrl: String, loginFlowUrl: String) {
|
||||
try {
|
||||
val res = mHttp.postLoginFlow(loginFlowUrl)
|
||||
fun loginFlow(baseUrl: String, loginFlowUrl: String) {
|
||||
val res = mHttp.postLoginFlow(loginFlowUrl)
|
||||
|
||||
// Check if 200 was received
|
||||
if (res.code != 200) {
|
||||
throw Exception("Server returned a ${res.code} status code. Please check your reverse proxy configuration and overwriteprotocol is correct.")
|
||||
}
|
||||
|
||||
// Get body as JSON
|
||||
val body = mHttp.bodyJson(res) ?: throw Exception("Failed to parse login flow response")
|
||||
|
||||
// Parse response body as JSON
|
||||
val pollObj = body.getJSONObject("poll")
|
||||
val pollToken = pollObj.getString("token")
|
||||
val pollUrl = pollObj.getString("endpoint")
|
||||
val loginUrl = body.getString("login")
|
||||
|
||||
// Open login page in browser
|
||||
mCtx.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(loginUrl)))
|
||||
|
||||
// Start polling in background
|
||||
Thread { pollLogin(pollUrl, pollToken, baseUrl) }.start()
|
||||
} catch (e: SocketTimeoutException) {
|
||||
toast("Failed to connect to login flow URL")
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "login: ", e)
|
||||
toast(e.message ?: "Unknown error")
|
||||
return
|
||||
// Check if 200 was received
|
||||
if (res.code != 200) {
|
||||
throw Exception("Login flow returned a ${res.code} status code. Check your reverse proxy configuration and overwriteprotocol is correct.")
|
||||
}
|
||||
|
||||
// Get body as JSON
|
||||
val body = mHttp.bodyJson(res) ?: throw Exception("Failed to parse login flow response")
|
||||
|
||||
// Parse response body as JSON
|
||||
val pollObj = body.getJSONObject("poll")
|
||||
val pollToken = pollObj.getString("token")
|
||||
val pollUrl = pollObj.getString("endpoint")
|
||||
val loginUrl = body.getString("login")
|
||||
|
||||
// Open login page in browser
|
||||
mCtx.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(loginUrl)))
|
||||
|
||||
// Start polling in background
|
||||
Thread { pollLogin(pollUrl, pollToken, baseUrl) }.start()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue