Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ImmichFrame.Core/Interfaces/IServerBehaviorSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public interface IServerBehaviorSettings
public List<string> Webcalendars { get; }
public int RefreshAlbumPeopleInterval { get; }
public string? WeatherApiKey { get; }
public string? WeatherApiKeyFile { get; }
public string? WeatherLatLong { get; }
public string? UnitSystem { get; }
public string? Webhook { get; }
Expand Down
3 changes: 3 additions & 0 deletions ImmichFrame.WebApi.Tests/Controllers/ConfigControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public void Setup()
Language = "de",
// Server-only values that must never surface in the client config:
WeatherApiKey = "secret-weather-key",
WeatherApiKeyFile = "/run/secrets/secret-weather-key-file",
WeatherLatLong = "1.23,4.56",
UnitSystem = "metric",
Webhook = "https://webhook.example.com/secret-hook",
Expand Down Expand Up @@ -128,11 +129,13 @@ public async Task GetConfig_ContainsNoSecrets()
{
Assert.That(json, Does.Not.Contain("secret-auth-token"));
Assert.That(json, Does.Not.Contain("secret-weather-key"));
Assert.That(json, Does.Not.Contain("secret-weather-key-file"));
Assert.That(json, Does.Not.Contain("secret-api-key"));
Assert.That(json, Does.Not.Contain("secret-hook"));
Assert.That(json, Does.Not.Contain("secret.ics"));
Assert.That(json, Does.Not.Contain("authenticationSecret"));
Assert.That(json, Does.Not.Contain("weatherApiKey"));
Assert.That(json, Does.Not.Contain("weatherApiKeyFile"));
Assert.That(json, Does.Not.Contain("apiKey"));
Assert.That(json, Does.Not.Contain("webhook"));
Assert.That(json, Does.Not.Contain("webcalendars"));
Expand Down
89 changes: 89 additions & 0 deletions ImmichFrame.WebApi.Tests/Helpers/Config/ConfigLoaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,95 @@ public void TestLoadConfigV2Yaml()
VerifyConfig(config, true, false);
}

[Test]
public void GeneralSettingsValidate_ReadsWeatherApiKeyFile()
{
var apiKeyFile = Path.GetTempFileName();
File.WriteAllText(apiKeyFile, "weather-api-key\n");

try
{
var settings = new GeneralSettings
{
WeatherApiKey = "",
WeatherApiKeyFile = apiKeyFile,
};

settings.Validate();

settings.WeatherApiKey.Should().Be("weather-api-key");
}
finally
{
File.Delete(apiKeyFile);
}
}

[Test]
public void GeneralSettingsValidate_ThrowsWhenWeatherApiKeyAndWeatherApiKeyFileAreBothSet()
{
var settings = new GeneralSettings
{
WeatherApiKey = "weather-api-key",
WeatherApiKeyFile = "/path/to/weather-api-key",
};

var exception = Assert.Throws<Exception>(() => settings.Validate());

exception!.Message.Should().Contain("Cannot specify both WeatherApiKey and WeatherApiKeyFile");
}

[Test]
public void ServerSettingsV1AdapterValidate_ReadsWeatherApiKeyFile()
{
var apiKeyFile = Path.GetTempFileName();
File.WriteAllText(apiKeyFile, "weather-api-key\n");

try
{
var settings = new ServerSettingsV1
{
WeatherApiKey = "",
WeatherApiKeyFile = apiKeyFile,
};

var adapter = new ServerSettingsV1Adapter(settings);
adapter.Validate();

adapter.GeneralSettings.WeatherApiKey.Should().Be("weather-api-key");
}
finally
{
File.Delete(apiKeyFile);
}
}

[Test]
public void ServerSettingsV1AdapterValidate_ThrowsWhenWeatherApiKeyAndWeatherApiKeyFileAreBothSet()
{
var apiKeyFile = Path.GetTempFileName();
File.WriteAllText(apiKeyFile, "weather-api-key\n");

try
{
var settings = new ServerSettingsV1
{
WeatherApiKey = "weather-api-key",
WeatherApiKeyFile = apiKeyFile,
};

var adapter = new ServerSettingsV1Adapter(settings);

var exception = Assert.Throws<Exception>(() => adapter.Validate());

exception!.Message.Should().Contain("Cannot specify both WeatherApiKey and WeatherApiKeyFile");
}
finally
{
File.Delete(apiKeyFile);
}
}
Comment thread
gvolpe marked this conversation as resolved.

private void VerifyConfig(IServerSettings serverSettings, bool usePrefix, bool expectNullApiKeyFile)
{
VerifyProperties(serverSettings.GeneralSettings);
Expand Down
1 change: 1 addition & 0 deletions ImmichFrame.WebApi.Tests/Resources/TestV1.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"Style": "Style_TEST",
"BaseFontSize": "BaseFontSize_TEST",
"WeatherApiKey": "WeatherApiKey_TEST",
"WeatherApiKeyFile": "WeatherApiKeyFile_TEST",
"ShowWeatherDescription": true,
"WeatherIconUrl": "WeatherIconUrl_TEST",
"UnitSystem": "UnitSystem_TEST",
Expand Down
1 change: 1 addition & 0 deletions ImmichFrame.WebApi.Tests/Resources/TestV2.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"PhotoDateFormat": "PhotoDateFormat_TEST",
"ImageLocationFormat": "ImageLocationFormat_TEST",
"WeatherApiKey": "WeatherApiKey_TEST",
"WeatherApiKeyFile": "WeatherApiKeyFile_TEST",
"UnitSystem": "UnitSystem_TEST",
"WeatherLatLong": "WeatherLatLong_TEST",
"Language": "Language_TEST",
Expand Down
1 change: 1 addition & 0 deletions ImmichFrame.WebApi.Tests/Resources/TestV2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ General:
PhotoDateFormat: PhotoDateFormat_TEST
ImageLocationFormat: ImageLocationFormat_TEST
WeatherApiKey: WeatherApiKey_TEST
WeatherApiKeyFile: WeatherApiKeyFile_TEST
UnitSystem: UnitSystem_TEST
WeatherLatLong: WeatherLatLong_TEST
Language: Language_TEST
Expand Down
15 changes: 14 additions & 1 deletion ImmichFrame.WebApi/Helpers/Config/ServerSettingsV1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class ServerSettingsV1 : IConfigSettable
public List<string> Webcalendars { get; set; } = new List<string>();
public int RefreshAlbumPeopleInterval { get; set; } = 12;
public string? WeatherApiKey { get; set; } = string.Empty;
public string? WeatherApiKeyFile { get; set; } = null;
public string? UnitSystem { get; set; } = "imperial";
public string? WeatherLatLong { get; set; } = "40.7128,74.0060";
public string Language { get; set; } = "en";
Expand Down Expand Up @@ -103,6 +104,7 @@ class GeneralSettingsV1Adapter(ServerSettingsV1 _delegate) : IGeneralSettings
public List<string> Webcalendars => _delegate.Webcalendars;
public int RefreshAlbumPeopleInterval => _delegate.RefreshAlbumPeopleInterval;
public string? WeatherApiKey => _delegate.WeatherApiKey;
public string? WeatherApiKeyFile => _delegate.WeatherApiKeyFile;
public string? WeatherLatLong => _delegate.WeatherLatLong;
public string? UnitSystem => _delegate.UnitSystem;
public string? Webhook => _delegate.Webhook;
Expand Down Expand Up @@ -136,6 +138,17 @@ class GeneralSettingsV1Adapter(ServerSettingsV1 _delegate) : IGeneralSettings
public string Layout => _delegate.Layout;
public string Language => _delegate.Language;

public void Validate() { }
public void Validate()
{
if (!string.IsNullOrWhiteSpace(_delegate.WeatherApiKeyFile))
{
if (!string.IsNullOrWhiteSpace(_delegate.WeatherApiKey))
{
throw new Exception("Cannot specify both WeatherApiKey and WeatherApiKeyFile. Please provide only one.");
}

_delegate.WeatherApiKey = File.ReadAllText(_delegate.WeatherApiKeyFile).Trim();
}
}
}
}
14 changes: 13 additions & 1 deletion ImmichFrame.WebApi/Models/ServerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,24 @@ public class GeneralSettings : IGeneralSettings, IConfigSettable
public List<string> Webcalendars { get; set; } = new();
public int RefreshAlbumPeopleInterval { get; set; } = 12;
public string? WeatherApiKey { get; set; } = string.Empty;
public string? WeatherApiKeyFile { get; set; } = null;
public string? UnitSystem { get; set; } = "imperial";
public string? WeatherLatLong { get; set; } = "40.7128,74.0060";
public string? Webhook { get; set; }
public string? AuthenticationSecret { get; set; }

public void Validate() { }
public void Validate()
{
if (!string.IsNullOrWhiteSpace(WeatherApiKeyFile))
{
if (!string.IsNullOrWhiteSpace(WeatherApiKey))
{
throw new Exception("Cannot specify both WeatherApiKey and WeatherApiKeyFile. Please provide only one.");
}

WeatherApiKey = File.ReadAllText(WeatherApiKeyFile).Trim();
}
}
}

public class ServerAccountSettings : IAccountSettings, IConfigSettable
Expand Down
4 changes: 4 additions & 0 deletions Install_Web.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ services:
restart: on-failure
ports:
- "8080:8080"
# To use WeatherApiKeyFile below, mount the host file at the configured container path.
# volumes:
# - /path/on/host/weather-api.key:/run/secrets/weather-api.key:ro
environment:
TZ: "Europe/Berlin"
ImmichServerUrl: "URL"
Expand Down Expand Up @@ -75,6 +78,7 @@ services:
# Style: "none"
# BaseFontSize: "17px"
# WeatherApiKey: ""
# WeatherApiKeyFile: "/run/secrets/weather-api.key"
# ShowWeatherDescription: "true"
# WeatherIconUrl: "https://openweathermap.org/img/wn/{IconId}.png"
# UnitSystem: "imperial"
Expand Down
1 change: 1 addition & 0 deletions docker/Settings.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"PhotoDateFormat": "MM/dd/yyyy",
"ImageLocationFormat": "City,State,Country",
"WeatherApiKey": "",
"WeatherApiKeyFile": null,
"UnitSystem": "imperial",
"WeatherLatLong": "40.730610,-73.935242",
"Webhook": null,
Expand Down
3 changes: 3 additions & 0 deletions docker/Settings.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ General:
RefreshAlbumPeopleInterval: 12
PhotoDateFormat: MM/dd/yyyy
ImageLocationFormat: 'City,State,Country'
# At most one of WeatherApiKey or WeatherApiKeyFile may be set.
WeatherApiKey: ''
# Optionally set WeatherApiKeyFile to a file path instead of WeatherApiKey.
# WeatherApiKeyFile: "/run/secrets/weather-api.key"
UnitSystem: imperial
WeatherLatLong: '40.730610,-73.935242'
Webhook: null
Expand Down
1 change: 1 addition & 0 deletions docker/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ ApiKey=KEY
# Style=none
# BaseFontSize=17px
# WeatherApiKey=
# WeatherApiKeyFile=/path/to/weather-api.key
# ShowWeatherDescription=true
# WeatherIconUrl=https://openweathermap.org/img/wn/{IconId}.png
# UnitSystem=imperial
Expand Down
7 changes: 5 additions & 2 deletions docs/docs/getting-started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@ General:
# Date format. See https://date-fns.org/v4.1.0/docs/format for more information.
PhotoDateFormat: 'MM/dd/yyyy' # string
ImageLocationFormat: 'City,State,Country'
# Get an API key from OpenWeatherMap: https://openweathermap.org/appid
# Get an API key from OpenWeatherMap: https://openweathermap.org/appid.
# At most one of WeatherApiKey or WeatherApiKeyFile may be set.
WeatherApiKey: '' # string
# Optionally set WeatherApiKeyFile to a file path instead of WeatherApiKey.
# WeatherApiKeyFile: '/run/secrets/weather-api.key' # string
# Imperial or metric system (Fahrenheit or Celsius)
UnitSystem: 'imperial' # 'imperial' | 'metric'
# Set the weather location with lat/lon.
Expand Down Expand Up @@ -160,7 +163,7 @@ For tags, use the full hierarchical path (the `value` field) as it appears in Im
- `"Travel/Europe"` - matches a tag "Europe" under parent "Travel"

### Weather
Weather is enabled by entering an API key. Get yours free from [OpenWeatherMap][openweathermap-url]
Weather is enabled by entering an API key. Get yours free from [OpenWeatherMap][openweathermap-url]. Set either `WeatherApiKey` or `WeatherApiKeyFile`, but not both. Leave both empty to disable weather.

### Calendar
If you are using Google Calendar, more information can be found [here](https://support.google.com/calendar/answer/37648?hl=en#zippy=%2Cget-your-calendar-view-only).
Expand Down
3 changes: 2 additions & 1 deletion docs/docs/getting-started/configurationV1.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ sidebar_position: 4
| Image | Interval | int | 45 | Image interval in seconds. How long a image is displayed in the frame. |
| Image | TransitionDuration | float | 2 | Duration in seconds. |
| [Weather](#weather) | WeatherApiKey | string | | Get an API key from [OpenWeatherMap][openweathermap-url]. |
| [Weather](#weather) | WeatherApiKeyFile | string | | Path to a file containing an API key from [OpenWeatherMap][openweathermap-url]. Cannot be used together with `WeatherApiKey`. |
| [Weather](#weather) | UnitSystem | imperial \| metric | imperial | Imperial or metric system. (Fahrenheit or degrees) |
| [Weather](#weather) | Language | string | en | 2 digit ISO code, sets the language of the weather description. |
| [Weather](#weather) | ShowWeatherDescription | boolean | true | Displays the description of the current weather. |
Expand Down Expand Up @@ -62,7 +63,7 @@ If this is enabled, the web api required the `Authorization`-Header with `Bearer
You can get the UUIDs from the URL of the album/person. For this URL: `https://demo.immich.app/albums/85c85b29-c95d-4a8b-90f7-c87da1d518ba` this is the UUID: `85c85b29-c95d-4a8b-90f7-c87da1d518ba`

### Weather
Weather is enabled by entering an API key. Get yours free from [OpenWeatherMap][openweathermap-url]
Weather is enabled by entering an API key. Get yours free from [OpenWeatherMap][openweathermap-url]. Set either `WeatherApiKey` or `WeatherApiKeyFile`.

### Calendar
If you are using Google Calendar, more information can be found [here](https://support.google.com/calendar/answer/37648?hl=en#zippy=%2Cget-your-calendar-view-only).
Expand Down