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.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.util.Log
|
||||
import app.alextran.immich.widget.model.*
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
@ -24,14 +25,23 @@ class ImmichAPI(cfg: ServerConfig) {
|
||||
|
||||
val serverURL = prefs.getString("widget_server_url", "") ?: ""
|
||||
val sessionKey = prefs.getString("widget_auth_token", "") ?: ""
|
||||
val customHeadersJSON = prefs.getString("widget_custom_headers", "") ?: ""
|
||||
|
||||
if (serverURL.isBlank() || sessionKey.isBlank()) {
|
||||
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(
|
||||
serverURL,
|
||||
sessionKey
|
||||
sessionKey,
|
||||
customHeaders
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -55,6 +65,12 @@ class ImmichAPI(cfg: ServerConfig) {
|
||||
val connection = (url.openConnection() as HttpURLConnection).apply {
|
||||
requestMethod = "POST"
|
||||
setRequestProperty("Content-Type", "application/json")
|
||||
|
||||
// Custom Headers
|
||||
serverConfig.customHeaders.forEach { (key, value) ->
|
||||
setRequestProperty(key, value)
|
||||
}
|
||||
|
||||
doOutput = true
|
||||
}
|
||||
|
||||
@ -75,6 +91,11 @@ class ImmichAPI(cfg: ServerConfig) {
|
||||
val url = buildRequestURL("/memories", listOf("for" to iso8601))
|
||||
val connection = (url.openConnection() as HttpURLConnection).apply {
|
||||
requestMethod = "GET"
|
||||
|
||||
// Custom Headers
|
||||
serverConfig.customHeaders.forEach { (key, value) ->
|
||||
setRequestProperty(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
val response = connection.inputStream.bufferedReader().readText()
|
||||
@ -94,6 +115,11 @@ class ImmichAPI(cfg: ServerConfig) {
|
||||
val url = buildRequestURL("/albums")
|
||||
val connection = (url.openConnection() as HttpURLConnection).apply {
|
||||
requestMethod = "GET"
|
||||
|
||||
// Custom Headers
|
||||
serverConfig.customHeaders.forEach { (key, value) ->
|
||||
setRequestProperty(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
val response = connection.inputStream.bufferedReader().readText()
|
||||
|
@ -55,7 +55,11 @@ data class WidgetEntry (
|
||||
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
|
||||
val kImageUUID = stringPreferencesKey("uuid")
|
||||
|
@ -104,10 +104,13 @@ struct Album: Codable, Equatable {
|
||||
// MARK: API
|
||||
|
||||
class ImmichAPI {
|
||||
typealias CustomHeaders = [String:String]
|
||||
struct ServerConfig {
|
||||
let serverEndpoint: String
|
||||
let sessionKey: String
|
||||
let customHeaders: CustomHeaders
|
||||
}
|
||||
|
||||
let serverConfig: ServerConfig
|
||||
|
||||
init() async throws {
|
||||
@ -122,10 +125,20 @@ class ImmichAPI {
|
||||
if serverURL == "" || sessionKey == "" {
|
||||
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(
|
||||
serverEndpoint: serverURL,
|
||||
sessionKey: sessionKey
|
||||
sessionKey: sessionKey,
|
||||
customHeaders: customHeaders
|
||||
)
|
||||
}
|
||||
|
||||
@ -155,6 +168,12 @@ class ImmichAPI {
|
||||
|
||||
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)
|
||||
async throws
|
||||
@ -174,7 +193,8 @@ class ImmichAPI {
|
||||
request.httpMethod = "POST"
|
||||
request.httpBody = try JSONEncoder().encode(filters)
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
applyHeaders(for: &request)
|
||||
|
||||
let (data, _) = try await URLSession.shared.data(for: request)
|
||||
|
||||
// decode data
|
||||
@ -196,6 +216,7 @@ class ImmichAPI {
|
||||
|
||||
var request = URLRequest(url: searchURL)
|
||||
request.httpMethod = "GET"
|
||||
applyHeaders(for: &request)
|
||||
|
||||
let (data, _) = try await URLSession.shared.data(for: request)
|
||||
|
||||
@ -254,7 +275,8 @@ class ImmichAPI {
|
||||
|
||||
var request = URLRequest(url: searchURL)
|
||||
request.httpMethod = "GET"
|
||||
|
||||
applyHeaders(for: &request)
|
||||
|
||||
let (data, _) = try await URLSession.shared.data(for: request)
|
||||
|
||||
// decode data
|
||||
|
@ -30,9 +30,10 @@ const int kTimelineAssetLoadBatchSize = 256;
|
||||
const int kTimelineAssetLoadOppositeSize = 64;
|
||||
|
||||
// Widget keys
|
||||
const String appShareGroupId = "group.app.immich.share";
|
||||
const String kWidgetAuthToken = "widget_auth_token";
|
||||
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
|
||||
// these are used to force a widget refresh
|
||||
|
@ -121,7 +121,9 @@ class AuthNotifier extends StateNotifier<AuthState> {
|
||||
Future<bool> saveAuthInfo({required String accessToken}) async {
|
||||
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
|
||||
String deviceId = Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid;
|
||||
|
@ -11,10 +11,11 @@ class WidgetService {
|
||||
|
||||
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.saveData(kWidgetServerEndpoint, serverURL);
|
||||
await _repository.saveData(kWidgetAuthToken, sessionKey);
|
||||
await _repository.saveData(kWidgetCustomHeaders, customHeaders);
|
||||
|
||||
// wait 3 seconds to ensure the widget is updated, dont block
|
||||
Future.delayed(const Duration(seconds: 3), refreshWidgets);
|
||||
@ -24,6 +25,7 @@ class WidgetService {
|
||||
await _repository.setAppGroupId(appShareGroupId);
|
||||
await _repository.saveData(kWidgetServerEndpoint, "");
|
||||
await _repository.saveData(kWidgetAuthToken, "");
|
||||
await _repository.saveData(kWidgetCustomHeaders, "");
|
||||
|
||||
// wait 3 seconds to ensure the widget is updated, dont block
|
||||
Future.delayed(const Duration(seconds: 3), refreshWidgets);
|
||||
|
Loading…
x
Reference in New Issue
Block a user