mirror of
https://github.com/immich-app/immich.git
synced 2025-08-11 09:16:31 -04:00
feat(mobile): use custom headers when connecting in widget
This commit is contained in:
parent
3e92e837f1
commit
43fb4f3bcf
@ -3,6 +3,7 @@ package app.alextran.immich.widget
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
|
import android.util.Log
|
||||||
import app.alextran.immich.widget.model.*
|
import app.alextran.immich.widget.model.*
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
@ -24,14 +25,23 @@ class ImmichAPI(cfg: ServerConfig) {
|
|||||||
|
|
||||||
val serverURL = prefs.getString("widget_server_url", "") ?: ""
|
val serverURL = prefs.getString("widget_server_url", "") ?: ""
|
||||||
val sessionKey = prefs.getString("widget_auth_token", "") ?: ""
|
val sessionKey = prefs.getString("widget_auth_token", "") ?: ""
|
||||||
|
val customHeadersJSON = prefs.getString("widget_custom_headers", "") ?: ""
|
||||||
|
|
||||||
if (serverURL.isBlank() || sessionKey.isBlank()) {
|
if (serverURL.isBlank() || sessionKey.isBlank()) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var customHeaders: Map<String, String> = HashMap<String, String>()
|
||||||
|
|
||||||
|
if (customHeadersJSON.isNotBlank()) {
|
||||||
|
val stringMapType = object : TypeToken<Map<String, String>>() {}.type
|
||||||
|
customHeaders = Gson().fromJson(customHeadersJSON, stringMapType)
|
||||||
|
}
|
||||||
|
|
||||||
return ServerConfig(
|
return ServerConfig(
|
||||||
serverURL,
|
serverURL,
|
||||||
sessionKey
|
sessionKey,
|
||||||
|
customHeaders
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,6 +65,12 @@ class ImmichAPI(cfg: ServerConfig) {
|
|||||||
val connection = (url.openConnection() as HttpURLConnection).apply {
|
val connection = (url.openConnection() as HttpURLConnection).apply {
|
||||||
requestMethod = "POST"
|
requestMethod = "POST"
|
||||||
setRequestProperty("Content-Type", "application/json")
|
setRequestProperty("Content-Type", "application/json")
|
||||||
|
|
||||||
|
// Custom Headers
|
||||||
|
serverConfig.customHeaders.forEach { (key, value) ->
|
||||||
|
setRequestProperty(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
doOutput = true
|
doOutput = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +91,11 @@ class ImmichAPI(cfg: ServerConfig) {
|
|||||||
val url = buildRequestURL("/memories", listOf("for" to iso8601))
|
val url = buildRequestURL("/memories", listOf("for" to iso8601))
|
||||||
val connection = (url.openConnection() as HttpURLConnection).apply {
|
val connection = (url.openConnection() as HttpURLConnection).apply {
|
||||||
requestMethod = "GET"
|
requestMethod = "GET"
|
||||||
|
|
||||||
|
// Custom Headers
|
||||||
|
serverConfig.customHeaders.forEach { (key, value) ->
|
||||||
|
setRequestProperty(key, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val response = connection.inputStream.bufferedReader().readText()
|
val response = connection.inputStream.bufferedReader().readText()
|
||||||
@ -94,6 +115,11 @@ class ImmichAPI(cfg: ServerConfig) {
|
|||||||
val url = buildRequestURL("/albums")
|
val url = buildRequestURL("/albums")
|
||||||
val connection = (url.openConnection() as HttpURLConnection).apply {
|
val connection = (url.openConnection() as HttpURLConnection).apply {
|
||||||
requestMethod = "GET"
|
requestMethod = "GET"
|
||||||
|
|
||||||
|
// Custom Headers
|
||||||
|
serverConfig.customHeaders.forEach { (key, value) ->
|
||||||
|
setRequestProperty(key, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val response = connection.inputStream.bufferedReader().readText()
|
val response = connection.inputStream.bufferedReader().readText()
|
||||||
|
@ -55,7 +55,11 @@ data class WidgetEntry (
|
|||||||
val deeplink: String?
|
val deeplink: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ServerConfig(val serverEndpoint: String, val sessionKey: String)
|
data class ServerConfig(
|
||||||
|
val serverEndpoint: String,
|
||||||
|
val sessionKey: String,
|
||||||
|
val customHeaders: Map<String, String>
|
||||||
|
)
|
||||||
|
|
||||||
// MARK: Widget State Keys
|
// MARK: Widget State Keys
|
||||||
val kImageUUID = stringPreferencesKey("uuid")
|
val kImageUUID = stringPreferencesKey("uuid")
|
||||||
|
@ -104,10 +104,13 @@ struct Album: Codable, Equatable {
|
|||||||
// MARK: API
|
// MARK: API
|
||||||
|
|
||||||
class ImmichAPI {
|
class ImmichAPI {
|
||||||
|
typealias CustomHeaders = [String:String]
|
||||||
struct ServerConfig {
|
struct ServerConfig {
|
||||||
let serverEndpoint: String
|
let serverEndpoint: String
|
||||||
let sessionKey: String
|
let sessionKey: String
|
||||||
|
let customHeaders: CustomHeaders
|
||||||
}
|
}
|
||||||
|
|
||||||
let serverConfig: ServerConfig
|
let serverConfig: ServerConfig
|
||||||
|
|
||||||
init() async throws {
|
init() async throws {
|
||||||
@ -123,9 +126,19 @@ class ImmichAPI {
|
|||||||
throw WidgetError.noLogin
|
throw WidgetError.noLogin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// custom headers come in the form of KV pairs in JSON
|
||||||
|
var customHeadersJSON = (defaults.string(forKey: "widget_custom_headers") ?? "")
|
||||||
|
var customHeaders: CustomHeaders = [:]
|
||||||
|
|
||||||
|
if customHeadersJSON != "",
|
||||||
|
let parsedHeaders = try? JSONDecoder().decode(CustomHeaders.self, from: customHeadersJSON.data(using: .utf8)!) {
|
||||||
|
customHeaders = parsedHeaders
|
||||||
|
}
|
||||||
|
|
||||||
serverConfig = ServerConfig(
|
serverConfig = ServerConfig(
|
||||||
serverEndpoint: serverURL,
|
serverEndpoint: serverURL,
|
||||||
sessionKey: sessionKey
|
sessionKey: sessionKey,
|
||||||
|
customHeaders: customHeaders
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,6 +169,12 @@ class ImmichAPI {
|
|||||||
return components?.url
|
return components?.url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyHeaders(for request: inout URLRequest) {
|
||||||
|
for (header, value) in serverConfig.customHeaders {
|
||||||
|
request.addValue(value, forHTTPHeaderField: header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func fetchSearchResults(with filters: SearchFilter = Album.NONE.filter)
|
func fetchSearchResults(with filters: SearchFilter = Album.NONE.filter)
|
||||||
async throws
|
async throws
|
||||||
-> [Asset]
|
-> [Asset]
|
||||||
@ -174,6 +193,7 @@ class ImmichAPI {
|
|||||||
request.httpMethod = "POST"
|
request.httpMethod = "POST"
|
||||||
request.httpBody = try JSONEncoder().encode(filters)
|
request.httpBody = try JSONEncoder().encode(filters)
|
||||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
|
applyHeaders(for: &request)
|
||||||
|
|
||||||
let (data, _) = try await URLSession.shared.data(for: request)
|
let (data, _) = try await URLSession.shared.data(for: request)
|
||||||
|
|
||||||
@ -196,6 +216,7 @@ class ImmichAPI {
|
|||||||
|
|
||||||
var request = URLRequest(url: searchURL)
|
var request = URLRequest(url: searchURL)
|
||||||
request.httpMethod = "GET"
|
request.httpMethod = "GET"
|
||||||
|
applyHeaders(for: &request)
|
||||||
|
|
||||||
let (data, _) = try await URLSession.shared.data(for: request)
|
let (data, _) = try await URLSession.shared.data(for: request)
|
||||||
|
|
||||||
@ -254,6 +275,7 @@ class ImmichAPI {
|
|||||||
|
|
||||||
var request = URLRequest(url: searchURL)
|
var request = URLRequest(url: searchURL)
|
||||||
request.httpMethod = "GET"
|
request.httpMethod = "GET"
|
||||||
|
applyHeaders(for: &request)
|
||||||
|
|
||||||
let (data, _) = try await URLSession.shared.data(for: request)
|
let (data, _) = try await URLSession.shared.data(for: request)
|
||||||
|
|
||||||
|
@ -30,9 +30,10 @@ const int kTimelineAssetLoadBatchSize = 256;
|
|||||||
const int kTimelineAssetLoadOppositeSize = 64;
|
const int kTimelineAssetLoadOppositeSize = 64;
|
||||||
|
|
||||||
// Widget keys
|
// Widget keys
|
||||||
|
const String appShareGroupId = "group.app.immich.share";
|
||||||
const String kWidgetAuthToken = "widget_auth_token";
|
const String kWidgetAuthToken = "widget_auth_token";
|
||||||
const String kWidgetServerEndpoint = "widget_server_url";
|
const String kWidgetServerEndpoint = "widget_server_url";
|
||||||
const String appShareGroupId = "group.app.immich.share";
|
const String kWidgetCustomHeaders = "widget_custom_headers";
|
||||||
|
|
||||||
// add widget identifiers here for new widgets
|
// add widget identifiers here for new widgets
|
||||||
// these are used to force a widget refresh
|
// these are used to force a widget refresh
|
||||||
|
@ -121,7 +121,9 @@ class AuthNotifier extends StateNotifier<AuthState> {
|
|||||||
Future<bool> saveAuthInfo({required String accessToken}) async {
|
Future<bool> saveAuthInfo({required String accessToken}) async {
|
||||||
await _apiService.setAccessToken(accessToken);
|
await _apiService.setAccessToken(accessToken);
|
||||||
|
|
||||||
await _widgetService.writeCredentials(Store.get(StoreKey.serverEndpoint), accessToken);
|
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
|
||||||
|
final customHeaders = Store.get(StoreKey.customHeaders);
|
||||||
|
await _widgetService.writeCredentials(serverEndpoint, accessToken, customHeaders);
|
||||||
|
|
||||||
// Get the deviceid from the store if it exists, otherwise generate a new one
|
// Get the deviceid from the store if it exists, otherwise generate a new one
|
||||||
String deviceId = Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid;
|
String deviceId = Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid;
|
||||||
|
@ -11,10 +11,11 @@ class WidgetService {
|
|||||||
|
|
||||||
const WidgetService(this._repository);
|
const WidgetService(this._repository);
|
||||||
|
|
||||||
Future<void> writeCredentials(String serverURL, String sessionKey) async {
|
Future<void> writeCredentials(String serverURL, String sessionKey, String customHeaders) async {
|
||||||
await _repository.setAppGroupId(appShareGroupId);
|
await _repository.setAppGroupId(appShareGroupId);
|
||||||
await _repository.saveData(kWidgetServerEndpoint, serverURL);
|
await _repository.saveData(kWidgetServerEndpoint, serverURL);
|
||||||
await _repository.saveData(kWidgetAuthToken, sessionKey);
|
await _repository.saveData(kWidgetAuthToken, sessionKey);
|
||||||
|
await _repository.saveData(kWidgetCustomHeaders, customHeaders);
|
||||||
|
|
||||||
// wait 3 seconds to ensure the widget is updated, dont block
|
// wait 3 seconds to ensure the widget is updated, dont block
|
||||||
Future.delayed(const Duration(seconds: 3), refreshWidgets);
|
Future.delayed(const Duration(seconds: 3), refreshWidgets);
|
||||||
@ -24,6 +25,7 @@ class WidgetService {
|
|||||||
await _repository.setAppGroupId(appShareGroupId);
|
await _repository.setAppGroupId(appShareGroupId);
|
||||||
await _repository.saveData(kWidgetServerEndpoint, "");
|
await _repository.saveData(kWidgetServerEndpoint, "");
|
||||||
await _repository.saveData(kWidgetAuthToken, "");
|
await _repository.saveData(kWidgetAuthToken, "");
|
||||||
|
await _repository.saveData(kWidgetCustomHeaders, "");
|
||||||
|
|
||||||
// wait 3 seconds to ensure the widget is updated, dont block
|
// wait 3 seconds to ensure the widget is updated, dont block
|
||||||
Future.delayed(const Duration(seconds: 3), refreshWidgets);
|
Future.delayed(const Duration(seconds: 3), refreshWidgets);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user