Skip to content

Commit 55dfcf3

Browse files
authored
Merge pull request #511 from jfly/issue-510-add-ApiKeyFile
Add a `ApiKeyFile` option
2 parents 1f803e2 + fc4fb84 commit 55dfcf3

File tree

12 files changed

+101
-24
lines changed

12 files changed

+101
-24
lines changed

ImmichFrame.Core/Interfaces/IServerSettings.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ public interface IServerSettings
44
{
55
public IEnumerable<IAccountSettings> Accounts { get; }
66
public IGeneralSettings GeneralSettings { get; }
7+
8+
public void Validate();
79
}
810

911
public interface IAccountSettings
1012
{
1113
public string ImmichServerUrl { get; }
1214
public string ApiKey { get; }
15+
public string? ApiKeyFile { get; }
1316
public bool ShowMemories { get; }
1417
public bool ShowFavorites { get; }
1518
public bool ShowArchived { get; }
@@ -20,6 +23,8 @@ public interface IAccountSettings
2023
public List<Guid> ExcludedAlbums { get; }
2124
public List<Guid> People { get; }
2225
public int? Rating { get; }
26+
27+
public void ValidateAndInitialize();
2328
}
2429

2530
public interface IGeneralSettings
@@ -57,5 +62,7 @@ public interface IGeneralSettings
5762
public bool ImageFill { get; }
5863
public string Layout { get; }
5964
public string Language { get; }
65+
66+
public void Validate();
6067
}
61-
}
68+
}

ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public void TestLoadConfigV1Json()
2929
{
3030
var config = _configLoader.LoadConfigJson<ServerSettingsV1>(Path.Combine(
3131
TestContext.CurrentContext.TestDirectory, "Resources/TestV1.json"));
32-
VerifyConfig(new ServerSettingsV1Adapter(config), false);
32+
VerifyConfig(new ServerSettingsV1Adapter(config), false, true);
3333
}
3434

3535
[Test]
@@ -39,15 +39,15 @@ public void TestLoadConfigEnv()
3939
TestContext.CurrentContext.TestDirectory, "Resources/TestV1.json"));
4040

4141
var config = _configLoader.LoadConfigFromDictionary<ServerSettingsV1>(ToDictionary(jsonConfig));
42-
VerifyConfig(new ServerSettingsV1Adapter(config), false);
42+
VerifyConfig(new ServerSettingsV1Adapter(config), false, true);
4343
}
4444

4545
[Test]
4646
public void TestLoadConfigV2Json()
4747
{
4848
var config = _configLoader.LoadConfigJson<ServerSettings>(Path.Combine(
4949
TestContext.CurrentContext.TestDirectory, "Resources/TestV2.json"));
50-
VerifyConfig(config, true);
50+
VerifyConfig(config, true, false);
5151
}
5252

5353
[Test]
@@ -65,26 +65,26 @@ public void TestLoadConfigV2Yaml()
6565
{
6666
var config = _configLoader.LoadConfigYaml<ServerSettings>(Path.Combine(
6767
TestContext.CurrentContext.TestDirectory, "Resources/TestV2.yml"));
68-
VerifyConfig(config, true);
68+
VerifyConfig(config, true, false);
6969
}
7070

71-
private void VerifyConfig(IServerSettings serverSettings, bool usePrefix)
71+
private void VerifyConfig(IServerSettings serverSettings, bool usePrefix, bool expectNullApiKeyFile)
7272
{
7373
VerifyProperties(serverSettings.GeneralSettings);
74-
VerifyAccounts(serverSettings.Accounts, usePrefix);
74+
VerifyAccounts(serverSettings.Accounts, usePrefix, expectNullApiKeyFile);
7575
}
7676

77-
private void VerifyAccounts(IEnumerable<IAccountSettings> accounts, bool usePrefix)
77+
private void VerifyAccounts(IEnumerable<IAccountSettings> accounts, bool usePrefix, bool expectNullApiKeyFile)
7878
{
7979
var idx = 1;
8080
foreach (var account in accounts)
8181
{
82-
VerifyProperties(account, usePrefix ? "Account" + idx + "." : "");
82+
VerifyProperties(account, usePrefix ? "Account" + idx + "." : "", expectNullApiKeyFile);
8383
idx++;
8484
}
8585
}
8686

87-
private void VerifyProperties(object o, string? prefix = "")
87+
private void VerifyProperties(object o, string? prefix = "", bool expectNullApiKeyFile = false)
8888
{
8989
foreach (var prop in o.GetType().GetProperties())
9090
{
@@ -107,7 +107,14 @@ private void VerifyProperties(object o, string? prefix = "")
107107
switch (type)
108108
{
109109
case var t when t == typeof(string):
110-
Assert.That(value, Is.EqualTo(prefix + prop.Name + "_TEST"), prop.Name);
110+
if (prop.Name.Equals("ApiKeyFile") && expectNullApiKeyFile)
111+
{
112+
Assert.That(value, Is.EqualTo(null), prop.Name);
113+
}
114+
else
115+
{
116+
Assert.That(value, Is.EqualTo(prefix + prop.Name + "_TEST"), prop.Name);
117+
}
111118
break;
112119
case var t when t == typeof(Boolean):
113120
Assert.That(value, Is.EqualTo(true), prop.Name);
@@ -171,4 +178,4 @@ public static IDictionary ToDictionary(object obj, bool ignoreNullValues = false
171178
return dictionary;
172179
}
173180

174-
}
181+
}

ImmichFrame.WebApi.Tests/Resources/TestV1.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,4 @@
5454
"Account2.ImmichServerUrl": "Account2.ImmichServerUrl_TEST",
5555
"Account2.ApiKey": "Account2.ApiKey_TEST",
5656
"Account2.ImagesFromDate": "Account2.ImagesFromDate_TEST"
57-
}
57+
}

ImmichFrame.WebApi.Tests/Resources/TestV2.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
{
4141
"ImmichServerUrl": "Account1.ImmichServerUrl_TEST",
4242
"ApiKey": "Account1.ApiKey_TEST",
43+
"ApiKeyFile": "Account1.ApiKeyFile_TEST",
4344
"ImagesFromDate": "2020-01-02",
4445
"ShowMemories": true,
4546
"ShowFavorites": true,
@@ -60,6 +61,7 @@
6061
{
6162
"ImmichServerUrl": "Account2.ImmichServerUrl_TEST",
6263
"ApiKey": "Account2.ApiKey_TEST",
64+
"ApiKeyFile": "Account2.ApiKeyFile_TEST",
6365
"ImagesFromDate": "2020-01-02",
6466
"ShowMemories": true,
6567
"ShowFavorites": true,
@@ -78,4 +80,4 @@
7880
]
7981
}
8082
]
81-
}
83+
}

ImmichFrame.WebApi.Tests/Resources/TestV2.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ General:
3737
Accounts:
3838
- ImmichServerUrl: Account1.ImmichServerUrl_TEST
3939
ApiKey: Account1.ApiKey_TEST
40+
ApiKeyFile: Account1.ApiKeyFile_TEST
4041
ImagesFromDate: '2020-01-02'
4142
ShowMemories: true
4243
ShowFavorites: true
@@ -52,6 +53,7 @@ Accounts:
5253
- 00000000-0000-0000-0000-000000000001
5354
- ImmichServerUrl: Account2.ImmichServerUrl_TEST
5455
ApiKey: Account2.ApiKey_TEST
56+
ApiKeyFile: Account2.ApiKeyFile_TEST
5557
ImagesFromDate: '2020-01-02'
5658
ShowMemories: true
5759
ShowFavorites: true
@@ -64,4 +66,4 @@ Accounts:
6466
ExcludedAlbums:
6567
- 00000000-0000-0000-0000-000000000001
6668
People:
67-
- 00000000-0000-0000-0000-000000000001
69+
- 00000000-0000-0000-0000-000000000001

ImmichFrame.WebApi/Helpers/Config/ConfigLoader.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ private string FindConfigFile(string dir, params string[] fileNames)
2121
?? Path.Combine(dir, fileNames.First());
2222
}
2323
public IServerSettings LoadConfig(string configPath)
24+
{
25+
var config = LoadConfigRaw(configPath);
26+
config.Validate();
27+
return config;
28+
}
29+
private IServerSettings LoadConfigRaw(string configPath)
2430
{
2531
var jsonConfigPath = FindConfigFile(configPath, "Settings.json");
2632
if (File.Exists(jsonConfigPath))
@@ -145,4 +151,4 @@ public IServerSettings LoadConfig(string configPath)
145151
throw new SettingsNotValidException($"Problem with parsing the settings: {ex.Message}", ex);
146152
}
147153
}
148-
}
154+
}

ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,20 @@ public class ServerSettingsV1Adapter(ServerSettingsV1 _delegate) : IServerSettin
6363
public IEnumerable<IAccountSettings> Accounts => new List<AccountSettingsV1Adapter> { new(_delegate) };
6464
public IGeneralSettings GeneralSettings => new GeneralSettingsV1Adapter(_delegate);
6565

66+
public void Validate()
67+
{
68+
GeneralSettings.Validate();
69+
foreach (var account in Accounts)
70+
{
71+
account.ValidateAndInitialize();
72+
}
73+
}
6674

6775
class AccountSettingsV1Adapter(ServerSettingsV1 _delegate) : IAccountSettings
6876
{
6977
public string ImmichServerUrl => _delegate.ImmichServerUrl;
7078
public string ApiKey => _delegate.ApiKey;
79+
public string? ApiKeyFile => null; // V1 settings didn't support paths to api keys.
7180
public bool ShowMemories => _delegate.ShowMemories;
7281
public bool ShowFavorites => _delegate.ShowFavorites;
7382
public bool ShowArchived => _delegate.ShowArchived;
@@ -78,6 +87,8 @@ class AccountSettingsV1Adapter(ServerSettingsV1 _delegate) : IAccountSettings
7887
public List<Guid> ExcludedAlbums => _delegate.ExcludedAlbums;
7988
public List<Guid> People => _delegate.People;
8089
public int? Rating => _delegate.Rating;
90+
91+
public void ValidateAndInitialize() { }
8192
}
8293

8394
class GeneralSettingsV1Adapter(ServerSettingsV1 _delegate) : IGeneralSettings
@@ -115,5 +126,7 @@ class GeneralSettingsV1Adapter(ServerSettingsV1 _delegate) : IGeneralSettings
115126
public bool ImageFill => _delegate.ImageFill;
116127
public string Layout => _delegate.Layout;
117128
public string Language => _delegate.Language;
129+
130+
public void Validate() { }
118131
}
119-
}
132+
}

ImmichFrame.WebApi/Models/ServerSettings.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ public class ServerSettings : IServerSettings, IConfigSettable
2323
[JsonIgnore]
2424
[YamlIgnore]
2525
public IEnumerable<IAccountSettings> Accounts => AccountsImpl;
26+
27+
public void Validate()
28+
{
29+
GeneralSettings.Validate();
30+
31+
foreach (var account in Accounts)
32+
{
33+
account.ValidateAndInitialize();
34+
}
35+
}
2636
}
2737

2838
public class GeneralSettings : IGeneralSettings, IConfigSettable
@@ -60,12 +70,15 @@ public class GeneralSettings : IGeneralSettings, IConfigSettable
6070
public string? WeatherLatLong { get; set; } = "40.7128,74.0060";
6171
public string? Webhook { get; set; }
6272
public string? AuthenticationSecret { get; set; }
73+
74+
public void Validate() { }
6375
}
6476

6577
public class ServerAccountSettings : IAccountSettings, IConfigSettable
6678
{
6779
public string ImmichServerUrl { get; set; } = string.Empty;
6880
public string ApiKey { get; set; } = string.Empty;
81+
public string? ApiKeyFile { get; set; } = null;
6982
public bool ShowMemories { get; set; } = false;
7083
public bool ShowFavorites { get; set; } = false;
7184
public bool ShowArchived { get; set; } = false;
@@ -77,4 +90,21 @@ public class ServerAccountSettings : IAccountSettings, IConfigSettable
7790
public List<Guid> ExcludedAlbums { get; set; } = new();
7891
public List<Guid> People { get; set; } = new();
7992
public int? Rating { get; set; }
80-
}
93+
94+
public void ValidateAndInitialize()
95+
{
96+
if (!string.IsNullOrWhiteSpace(ApiKeyFile))
97+
{
98+
if (!string.IsNullOrWhiteSpace(ApiKey))
99+
{
100+
throw new Exception("Cannot specify both ApiKey and ApiKeyFile. Please provide only one.");
101+
}
102+
ApiKey = File.ReadAllText(ApiKeyFile).Trim();
103+
}
104+
105+
if (string.IsNullOrWhiteSpace(ApiKey))
106+
{
107+
throw new InvalidOperationException("Either ApiKey or ApiKeyFile must be provided.");
108+
}
109+
}
110+
}

docker/Settings.example.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
"Accounts": [
4040
{
4141
"ImmichServerUrl": "REQUIRED",
42-
"ApiKey": "REQUIRED",
42+
"ApiKey": "super-secret-api-key",
43+
"ApiKeyFile": "/path/to/api.key",
4344
"ImagesFromDate": null,
4445
"ShowMemories": false,
4546
"ShowFavorites": false,
@@ -58,4 +59,4 @@
5859
]
5960
}
6061
]
61-
}
62+
}

docker/Settings.example.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ General:
3535
Layout: splitview
3636
Accounts:
3737
- ImmichServerUrl: REQUIRED
38-
ApiKey: REQUIRED
38+
# Exactly one of ApiKey or ApiKeyFile must be set.
39+
ApiKey: "super-secret-api-key"
40+
# ApiKeyFile: "/path/to/api.key"
41+
3942
ImagesFromDate: null
4043
ShowMemories: false
4144
ShowFavorites: false

0 commit comments

Comments
 (0)