mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-05 20:51:13 -07:00
Merge branch 'develop' into cypress-13
This commit is contained in:
commit
fefc768aa5
25 changed files with 659 additions and 185 deletions
27
README.md
27
README.md
|
@ -659,10 +659,10 @@ Here are some of the features Ombi has:
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/sussycatgirl">
|
<a href="https://github.com/Drewster727">
|
||||||
<img src="https://avatars.githubusercontent.com/u/26145882?v=4" width="50;" alt="sussycatgirl"/>
|
<img src="https://avatars.githubusercontent.com/u/4528753?v=4" width="50;" alt="Drewster727"/>
|
||||||
<br />
|
<br />
|
||||||
<sub><b>Lea</b></sub>
|
<sub><b>Drew</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
|
@ -759,13 +759,6 @@ Here are some of the features Ombi has:
|
||||||
</a>
|
</a>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
|
||||||
<a href="https://github.com/AlexandrePicavet">
|
|
||||||
<img src="https://avatars.githubusercontent.com/u/25816980?v=4" width="50;" alt="AlexandrePicavet"/>
|
|
||||||
<br />
|
|
||||||
<sub><b>Alexandre Picavet</b></sub>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/XanderStrike">
|
<a href="https://github.com/XanderStrike">
|
||||||
<img src="https://avatars.githubusercontent.com/u/1565303?v=4" width="50;" alt="XanderStrike"/>
|
<img src="https://avatars.githubusercontent.com/u/1565303?v=4" width="50;" alt="XanderStrike"/>
|
||||||
|
@ -787,6 +780,13 @@ Here are some of the features Ombi has:
|
||||||
<sub><b>Abe Kline</b></sub>
|
<sub><b>Abe Kline</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/sussycatgirl">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/26145882?v=4" width="50;" alt="sussycatgirl"/>
|
||||||
|
<br />
|
||||||
|
<sub><b>Lea</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a href="https://github.com/kmlucy">
|
<a href="https://github.com/kmlucy">
|
||||||
<img src="https://avatars.githubusercontent.com/u/13952475?v=4" width="50;" alt="kmlucy"/>
|
<img src="https://avatars.githubusercontent.com/u/13952475?v=4" width="50;" alt="kmlucy"/>
|
||||||
|
@ -901,13 +901,6 @@ Here are some of the features Ombi has:
|
||||||
<br />
|
<br />
|
||||||
<sub><b>Eli</b></sub>
|
<sub><b>Eli</b></sub>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
|
||||||
<td align="center">
|
|
||||||
<a href="https://github.com/Drewster727">
|
|
||||||
<img src="https://avatars.githubusercontent.com/u/4528753?v=4" width="50;" alt="Drewster727"/>
|
|
||||||
<br />
|
|
||||||
<sub><b>Drew</b></sub>
|
|
||||||
</a>
|
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
<!-- readme: collaborators,contributors -end -->
|
<!-- readme: collaborators,contributors -end -->
|
||||||
|
|
2
src/.idea/.idea.Ombi/.idea/indexLayout.xml
generated
2
src/.idea/.idea.Ombi/.idea/indexLayout.xml
generated
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ContentModelUserStore">
|
<component name="UserContentModel">
|
||||||
<attachedFolders />
|
<attachedFolders />
|
||||||
<explicitIncludes />
|
<explicitIncludes />
|
||||||
<explicitExcludes />
|
<explicitExcludes />
|
||||||
|
|
138
src/.idea/.idea.Ombi/.idea/workspace.xml
generated
138
src/.idea/.idea.Ombi/.idea/workspace.xml
generated
|
@ -1,25 +1,22 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="AutoImportSettings">
|
||||||
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment="">
|
<list default="true" id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment="">
|
||||||
<change afterPath="$PROJECT_DIR$/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/Ombi.Core/Models/Search/V2/Music/ArtistInformation.cs" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/contentModel.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/contentModel.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/config/applicationhost.config" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/config/applicationhost.config" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/wizard/welcome/welcome.component.html" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/wizard/welcome/welcome.component.html" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Ombi.Api.MusicBrainz/Models/Artist/ArtistInformation.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.Api.MusicBrainz/Models/Artist/ArtistInformation.cs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Ombi/Controllers/V2/WizardController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/Controllers/V2/WizardController.cs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Ombi.DependencyInjection/IocExtensions.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.DependencyInjection/IocExtensions.cs" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/media-details/components/artist/artist-details.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/media-details/components/artist/artist-details.component.ts" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/services/searchV2.service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/services/searchV2.service.ts" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" afterDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="DpaMonitoringSettings">
|
||||||
|
<option name="firstShow" value="false" />
|
||||||
|
</component>
|
||||||
<component name="FileEditorManager">
|
<component name="FileEditorManager">
|
||||||
<leaf>
|
<leaf>
|
||||||
<file pinned="false" current-in-tab="false">
|
<file pinned="false" current-in-tab="false">
|
||||||
|
@ -237,27 +234,63 @@
|
||||||
<component name="Git.Settings">
|
<component name="Git.Settings">
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="GitToolBoxStore">
|
||||||
|
<option name="recentBranches">
|
||||||
|
<RecentBranches>
|
||||||
|
<option name="branchesForRepo">
|
||||||
|
<list>
|
||||||
|
<RecentBranchesForRepo>
|
||||||
|
<option name="branches">
|
||||||
|
<list>
|
||||||
|
<RecentBranch>
|
||||||
|
<option name="branchName" value="wizard-database" />
|
||||||
|
<option name="lastUsedInstant" value="1735917525" />
|
||||||
|
</RecentBranch>
|
||||||
|
<RecentBranch>
|
||||||
|
<option name="branchName" value="develop" />
|
||||||
|
<option name="lastUsedInstant" value="1735917524" />
|
||||||
|
</RecentBranch>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="repositoryRootUrl" value="file://$PROJECT_DIR$/.." />
|
||||||
|
</RecentBranchesForRepo>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</RecentBranches>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="GithubProjectSettings">
|
||||||
|
<option name="branchProtectionPatterns">
|
||||||
|
<list>
|
||||||
|
<option value="master" />
|
||||||
|
<option value="develop" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
<component name="HighlightingSettingsPerFile">
|
<component name="HighlightingSettingsPerFile">
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Helpers.Tests/EmbyHelperTests.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/990126b794024fe2bd16aebdd37eba1d7b600/93/25662f04/ServerVersion.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/3bd4df5aff92cabbc4d630be64227073db1b8539b3a1e47786b4b189d7cdb7/DbContext.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Schedule.Tests/OmbiQuartzTests.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/449b441523c469ed34ff5a5e14f0bafcd8f097aa463655303dc19048fa44ac3/EntityFrameworkServiceCollectionExtensions.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/Models/Artist/ArtistInformation.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/7d81b2d4f22bee75e5438c707251ae43cb0974c207db91ffc159118c84b4eb9/ServiceProvider.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/a424e6912048b4cd25715f158e789aae24db5c2911d9e622d39bc6ac3246c6/MySqlConnectionStringBuilder.cs" root0="SKIP_HIGHLIGHTING" />
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.DependencyInjection/IocExtensions.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/bd1d5c50194fea68ff3559c160230b0ab50f5acf4ce3061bffd6d62958e2182/ExceptionDispatchInfo.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/e9881a453a581134c1a18331ac1f8f1201a5382a685bf2a40777fa22619/DbContextOptions`.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/IMultiSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/IMusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/IMusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Core/Models/Search/V2/Music/ArtistInformation.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="mock:///Dummy.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/RecentlyAddedEngine.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="mock:///Dummy.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/MusicSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/MusicSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi/Program.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/RecentlyAddedEngine.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/UserStatsEngine.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/UserStatsEngine.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="mock:///Dummy.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/IMultiSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="file://$PROJECT_DIR$/Ombi.Core/Models/Search/V2/Music/ArtistInformation.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="file://$PROJECT_DIR$/Ombi.DependencyInjection/IocExtensions.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="file://$PROJECT_DIR$/Ombi.Helpers.Tests/EmbyHelperTests.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="file://$PROJECT_DIR$/Ombi.Schedule.Tests/OmbiQuartzTests.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="file://$PROJECT_DIR$/Ombi/Program.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
</component>
|
</component>
|
||||||
<component name="IdeDocumentHistory">
|
<component name="IdeDocumentHistory">
|
||||||
<option name="CHANGED_PATHS">
|
<option name="CHANGED_PATHS">
|
||||||
|
@ -343,27 +376,14 @@
|
||||||
<pane id="FileSystemExplorer" />
|
<pane id="FileSystemExplorer" />
|
||||||
</panes>
|
</panes>
|
||||||
</component>
|
</component>
|
||||||
<component name="PropertiesComponent">
|
<component name="PropertiesComponent"><![CDATA[{
|
||||||
<property name="ASKED_SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
|
"keyToString": {
|
||||||
<property name="Rider.DefaultBreakpoints.AreToggled" value="true" />
|
".NET Launch Settings Profile.Ombi.executor": "Run",
|
||||||
<property name="Rider.ProjectViewActivator.IsNotFirstRun" value="true" />
|
"git-widget-placeholder": "#5208 on wizard-database",
|
||||||
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
|
"node.js.selected.package.tslint": "(autodetect)"
|
||||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
}
|
||||||
<property name="nodejs_package_manager_path" value="npm" />
|
}]]></component>
|
||||||
</component>
|
<component name="RunManager" selected=".NET Launch Settings Profile.Ombi">
|
||||||
<component name="RunDashboard">
|
|
||||||
<option name="ruleStates">
|
|
||||||
<list>
|
|
||||||
<RuleState>
|
|
||||||
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
|
|
||||||
</RuleState>
|
|
||||||
<RuleState>
|
|
||||||
<option name="name" value="StatusDashboardGroupingRule" />
|
|
||||||
</RuleState>
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
<component name="RunManager" selected=".NET Launch Settings Profile.Ombi: IIS Express">
|
|
||||||
<configuration name="Ombi" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
|
<configuration name="Ombi" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
|
||||||
<option name="LAUNCH_PROFILE_PROJECT_FILE_PATH" value="$PROJECT_DIR$/Ombi/Ombi.csproj" />
|
<option name="LAUNCH_PROFILE_PROJECT_FILE_PATH" value="$PROJECT_DIR$/Ombi/Ombi.csproj" />
|
||||||
<option name="LAUNCH_PROFILE_TFM" value=".NETCoreApp,Version=v2.2" />
|
<option name="LAUNCH_PROFILE_TFM" value=".NETCoreApp,Version=v2.2" />
|
||||||
|
@ -376,7 +396,7 @@
|
||||||
<option name="SEND_DEBUG_REQUEST" value="1" />
|
<option name="SEND_DEBUG_REQUEST" value="1" />
|
||||||
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
|
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" enabled="true" />
|
<option name="Build" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
<configuration name="Ombi: IIS Express" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
|
<configuration name="Ombi: IIS Express" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
|
||||||
|
@ -391,7 +411,7 @@
|
||||||
<option name="SEND_DEBUG_REQUEST" value="1" />
|
<option name="SEND_DEBUG_REQUEST" value="1" />
|
||||||
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
|
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" enabled="true" />
|
<option name="Build" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
<configuration name="Ombi.Schedule.Tests" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
|
<configuration name="Ombi.Schedule.Tests" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
|
||||||
|
@ -406,7 +426,7 @@
|
||||||
<option name="SEND_DEBUG_REQUEST" value="1" />
|
<option name="SEND_DEBUG_REQUEST" value="1" />
|
||||||
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
|
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" enabled="true" />
|
<option name="Build" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
<configuration name="Ombi.Schedule.Tests: IIS Express" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
|
<configuration name="Ombi.Schedule.Tests: IIS Express" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
|
||||||
|
@ -421,7 +441,7 @@
|
||||||
<option name="SEND_DEBUG_REQUEST" value="1" />
|
<option name="SEND_DEBUG_REQUEST" value="1" />
|
||||||
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
|
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" enabled="true" />
|
<option name="Build" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
<configuration name="Ombi.Updater" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
|
<configuration name="Ombi.Updater" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
|
||||||
|
@ -436,7 +456,7 @@
|
||||||
<option name="SEND_DEBUG_REQUEST" value="1" />
|
<option name="SEND_DEBUG_REQUEST" value="1" />
|
||||||
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
|
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Build" enabled="true" />
|
<option name="Build" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
|
@ -492,9 +512,7 @@
|
||||||
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="4" weight="0.25" />
|
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="4" weight="0.25" />
|
||||||
</layout>
|
</layout>
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="UnityProjectConfiguration" hasMinimizedUI="false" />
|
||||||
<option name="version" value="1" />
|
|
||||||
</component>
|
|
||||||
<component name="XDebuggerManager">
|
<component name="XDebuggerManager">
|
||||||
<breakpoint-manager>
|
<breakpoint-manager>
|
||||||
<breakpoints>
|
<breakpoints>
|
||||||
|
@ -505,7 +523,7 @@
|
||||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||||
<url>file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs</url>
|
<url>file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs</url>
|
||||||
<line>48</line>
|
<line>48</line>
|
||||||
<properties documentPath="$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs" initialLine="48">
|
<properties documentPath="$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs">
|
||||||
<startOffsets>
|
<startOffsets>
|
||||||
<option value="1518" />
|
<option value="1518" />
|
||||||
</startOffsets>
|
</startOffsets>
|
||||||
|
@ -518,7 +536,7 @@
|
||||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||||
<url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url>
|
<url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url>
|
||||||
<line>59</line>
|
<line>59</line>
|
||||||
<properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" initialLine="59">
|
<properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs">
|
||||||
<startOffsets>
|
<startOffsets>
|
||||||
<option value="2276" />
|
<option value="2276" />
|
||||||
</startOffsets>
|
</startOffsets>
|
||||||
|
@ -531,7 +549,7 @@
|
||||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||||
<url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url>
|
<url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url>
|
||||||
<line>49</line>
|
<line>49</line>
|
||||||
<properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" initialLine="49">
|
<properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs">
|
||||||
<startOffsets>
|
<startOffsets>
|
||||||
<option value="2001" />
|
<option value="2001" />
|
||||||
</startOffsets>
|
</startOffsets>
|
||||||
|
@ -544,7 +562,7 @@
|
||||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||||
<url>file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs</url>
|
<url>file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs</url>
|
||||||
<line>30</line>
|
<line>30</line>
|
||||||
<properties documentPath="$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs" initialLine="30">
|
<properties documentPath="$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs">
|
||||||
<startOffsets>
|
<startOffsets>
|
||||||
<option value="917" />
|
<option value="917" />
|
||||||
</startOffsets>
|
</startOffsets>
|
||||||
|
|
67
src/Ombi.Core/Helpers/DatabaseConfigurationSetup.cs
Normal file
67
src/Ombi.Core/Helpers/DatabaseConfigurationSetup.cs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
|
||||||
|
using Ombi.Core.Models;
|
||||||
|
using Polly;
|
||||||
|
using Pomelo.EntityFrameworkCore.MySql.Storage.Internal;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Helpers;
|
||||||
|
|
||||||
|
public static class DatabaseConfigurationSetup
|
||||||
|
{
|
||||||
|
public static void ConfigurePostgres(DbContextOptionsBuilder options, PerDatabaseConfiguration config)
|
||||||
|
{
|
||||||
|
options.UseNpgsql(config.ConnectionString, b =>
|
||||||
|
{
|
||||||
|
b.EnableRetryOnFailure();
|
||||||
|
}).ReplaceService<ISqlGenerationHelper, NpgsqlCaseInsensitiveSqlGenerationHelper>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ConfigureMySql(DbContextOptionsBuilder options, PerDatabaseConfiguration config)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(config.ConnectionString))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("ConnectionString for the MySql/Mariadb database is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
options.UseMySql(config.ConnectionString, GetServerVersion(config.ConnectionString), b =>
|
||||||
|
{
|
||||||
|
//b.CharSetBehavior(Pomelo.EntityFrameworkCore.MySql.Infrastructure.CharSetBehavior.NeverAppend); // ##ISSUE, link to migrations?
|
||||||
|
b.EnableRetryOnFailure();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ServerVersion GetServerVersion(string connectionString)
|
||||||
|
{
|
||||||
|
// Workaround Windows bug, that can lead to the following exception:
|
||||||
|
//
|
||||||
|
// MySqlConnector.MySqlException (0x80004005): SSL Authentication Error
|
||||||
|
// ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
|
||||||
|
// ---> System.ComponentModel.Win32Exception (0x8009030F): The message or signature supplied for verification has been altered
|
||||||
|
//
|
||||||
|
// See https://github.com/dotnet/runtime/issues/17005#issuecomment-305848835
|
||||||
|
//
|
||||||
|
// Also workaround for the fact, that ServerVersion.AutoDetect() does not use any retrying strategy.
|
||||||
|
ServerVersion serverVersion = null;
|
||||||
|
#pragma warning disable EF1001
|
||||||
|
var retryPolicy = Policy.Handle<Exception>(exception => MySqlTransientExceptionDetector.ShouldRetryOn(exception))
|
||||||
|
#pragma warning restore EF1001
|
||||||
|
.WaitAndRetry(3, (count, context) => TimeSpan.FromMilliseconds(count * 250));
|
||||||
|
|
||||||
|
serverVersion = retryPolicy.Execute(() => serverVersion = ServerVersion.AutoDetect(connectionString));
|
||||||
|
|
||||||
|
return serverVersion;
|
||||||
|
}
|
||||||
|
public class NpgsqlCaseInsensitiveSqlGenerationHelper : NpgsqlSqlGenerationHelper
|
||||||
|
{
|
||||||
|
const string EFMigrationsHisory = "__EFMigrationsHistory";
|
||||||
|
public NpgsqlCaseInsensitiveSqlGenerationHelper(RelationalSqlGenerationHelperDependencies dependencies)
|
||||||
|
: base(dependencies) { }
|
||||||
|
public override string DelimitIdentifier(string identifier) =>
|
||||||
|
base.DelimitIdentifier(identifier == EFMigrationsHisory ? identifier : identifier.ToLower());
|
||||||
|
public override void DelimitIdentifier(StringBuilder builder, string identifier)
|
||||||
|
=> base.DelimitIdentifier(builder, identifier == EFMigrationsHisory ? identifier : identifier.ToLower());
|
||||||
|
}
|
||||||
|
}
|
10
src/Ombi.Core/Helpers/FileSystem.cs
Normal file
10
src/Ombi.Core/Helpers/FileSystem.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ombi.Core.Helpers;
|
||||||
|
|
||||||
|
public class FileSystem : IFileSystem
|
||||||
|
{
|
||||||
|
public bool FileExists(string path)
|
||||||
|
{
|
||||||
|
return System.IO.File.Exists(path);
|
||||||
|
}
|
||||||
|
// Implement other file system operations as needed
|
||||||
|
}
|
7
src/Ombi.Core/Helpers/IFileSystem.cs
Normal file
7
src/Ombi.Core/Helpers/IFileSystem.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Ombi.Core.Helpers;
|
||||||
|
|
||||||
|
public interface IFileSystem
|
||||||
|
{
|
||||||
|
bool FileExists(string path);
|
||||||
|
// Add other file system operations as needed
|
||||||
|
}
|
40
src/Ombi.Core/Models/DatabaseConfiguration.cs
Normal file
40
src/Ombi.Core/Models/DatabaseConfiguration.cs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Models;
|
||||||
|
|
||||||
|
public class DatabaseConfiguration
|
||||||
|
{
|
||||||
|
public const string SqliteDatabase = "Sqlite";
|
||||||
|
|
||||||
|
public DatabaseConfiguration()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseConfiguration(string defaultSqlitePath)
|
||||||
|
{
|
||||||
|
OmbiDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "Ombi.db")}");
|
||||||
|
SettingsDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "OmbiSettings.db")}");
|
||||||
|
ExternalDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "OmbiExternal.db")}");
|
||||||
|
}
|
||||||
|
public PerDatabaseConfiguration OmbiDatabase { get; set; }
|
||||||
|
public PerDatabaseConfiguration SettingsDatabase { get; set; }
|
||||||
|
public PerDatabaseConfiguration ExternalDatabase { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PerDatabaseConfiguration
|
||||||
|
{
|
||||||
|
public PerDatabaseConfiguration(string type, string connectionString)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
ConnectionString = connectionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used in Deserialization
|
||||||
|
public PerDatabaseConfiguration()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
public string Type { get; set; }
|
||||||
|
public string ConnectionString { get; set; }
|
||||||
|
}
|
69
src/Ombi.Core/Services/DatabaseConfigurationService.cs
Normal file
69
src/Ombi.Core/Services/DatabaseConfigurationService.cs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Ombi.Core.Helpers;
|
||||||
|
using Ombi.Core.Models;
|
||||||
|
using Ombi.Helpers;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Services;
|
||||||
|
|
||||||
|
public class DatabaseConfigurationService : IDatabaseConfigurationService
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
public DatabaseConfigurationService(
|
||||||
|
ILogger<DatabaseConfigurationService> logger,
|
||||||
|
IFileSystem fileSystem)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ConfigureDatabase(string databaseType, string connectionString, CancellationToken token)
|
||||||
|
{
|
||||||
|
var i = StartupSingleton.Instance;
|
||||||
|
if (string.IsNullOrEmpty(i.StoragePath))
|
||||||
|
{
|
||||||
|
i.StoragePath = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var databaseFileLocation = Path.Combine(i.StoragePath, "database.json");
|
||||||
|
if (_fileSystem.FileExists(databaseFileLocation))
|
||||||
|
{
|
||||||
|
var error = $"The database file at '{databaseFileLocation}' already exists";
|
||||||
|
_logger.LogError(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var configuration = new DatabaseConfiguration
|
||||||
|
{
|
||||||
|
ExternalDatabase = new PerDatabaseConfiguration(databaseType, connectionString),
|
||||||
|
OmbiDatabase = new PerDatabaseConfiguration(databaseType, connectionString),
|
||||||
|
SettingsDatabase = new PerDatabaseConfiguration(databaseType, connectionString)
|
||||||
|
};
|
||||||
|
|
||||||
|
var json = JsonConvert.SerializeObject(configuration, Formatting.Indented);
|
||||||
|
|
||||||
|
_logger.LogInformation("Writing database configuration to file");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await File.WriteAllTextAsync(databaseFileLocation, json, token);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Failed to write database configuration to file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Database configuration written to file");
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
11
src/Ombi.Core/Services/IDatabaseConfigurationService.cs
Normal file
11
src/Ombi.Core/Services/IDatabaseConfigurationService.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Services;
|
||||||
|
|
||||||
|
public interface IDatabaseConfigurationService
|
||||||
|
{
|
||||||
|
const string MySqlDatabase = "MySQL";
|
||||||
|
const string PostgresDatabase = "Postgres";
|
||||||
|
Task<bool> ConfigureDatabase(string databaseType, string connectionString, CancellationToken token);
|
||||||
|
}
|
|
@ -236,6 +236,8 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddScoped<IFeatureService, FeatureService>();
|
services.AddScoped<IFeatureService, FeatureService>();
|
||||||
services.AddTransient<IRecentlyRequestedService, RecentlyRequestedService>();
|
services.AddTransient<IRecentlyRequestedService, RecentlyRequestedService>();
|
||||||
services.AddTransient<IPlexService, PlexService>();
|
services.AddTransient<IPlexService, PlexService>();
|
||||||
|
services.AddSingleton<IFileSystem, FileSystem>();
|
||||||
|
services.AddSingleton<IDatabaseConfigurationService, DatabaseConfigurationService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RegisterJobs(this IServiceCollection services)
|
public static void RegisterJobs(this IServiceCollection services)
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
<div class="mediaserver-container">
|
||||||
|
<div class="left-container mediaserver">
|
||||||
|
<i class="fa fa-database text-logo"></i>
|
||||||
|
</div>
|
||||||
|
<div class="right-container mediaserver">
|
||||||
|
<div class="right-container-content mediaserver">
|
||||||
|
<h1>Choose a Database</h1>
|
||||||
|
<h4>
|
||||||
|
SQLite is the default option and the easiest to set up, as it requires no additional configuration.
|
||||||
|
<br>However, it has significant limitations, including potential performance issues and database locking.
|
||||||
|
<br>While many users start with SQLite and later migrate to MySQL or MariaDB, we <b>recommend</b> beginning with MySQL or MariaDB from the start for a more robust and scalable experience.
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
For more information on using alternate databases, <a target="_blank" href="https://docs.ombi.app/info/alternate-databases/">see the documentation.</a>
|
||||||
|
</h4>
|
||||||
|
<form [formGroup]="form">
|
||||||
|
<mat-tab-group (selectedTabChange)="tabChange($event)">
|
||||||
|
<mat-tab label="SQLite">
|
||||||
|
<p class="space-or">
|
||||||
|
Just press next to continue with SQLite
|
||||||
|
</p>
|
||||||
|
</mat-tab>
|
||||||
|
<mat-tab label="MySQL/MariaDB">
|
||||||
|
<p class="space-or">
|
||||||
|
Please enter your MySQL/MariaDB connection details below
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="text" formControlName="host" id="host" placeholder="Host">
|
||||||
|
<mat-error>This field is required</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="number" formControlName="port" id="port" placeholder="Port">
|
||||||
|
<mat-error>This field is required</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="text" formControlName="name" id="database" placeholder="Database Name">
|
||||||
|
<mat-error>This field is required</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="text" formControlName="user" id="user" placeholder="User">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="password" formControlName="password" id="password" placeholder="Password">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<p>{{connectionString | async}}</p>
|
||||||
|
<div style="text-align: center; margin-top: 20px">
|
||||||
|
<button (click)="save()" id="databaseSave" mat-raised-button color="accent" type="button" class="viewon-btn database" [disabled]="form.invalid">Save</button>
|
||||||
|
<div id="spinner"></div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
|
||||||
|
<mat-tab label="Postgres">
|
||||||
|
<p class="space-or">
|
||||||
|
Please enter your Postgres connection details below
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="text" formControlName="host" id="host" placeholder="Host">
|
||||||
|
<mat-error>This field is required</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="number" formControlName="port" id="port" placeholder="Port">
|
||||||
|
<mat-error>This field is required</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="text" formControlName="name" id="database" placeholder="Database Name">
|
||||||
|
<mat-error>This field is required</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="text" formControlName="user" id="user" placeholder="User">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput type="password" formControlName="password" id="password" placeholder="Password">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<p>{{connectionString | async}}</p>
|
||||||
|
<div style="text-align: center; margin-top: 20px">
|
||||||
|
<button (click)="save()" id="databaseSave" mat-raised-button color="accent" type="button" class="viewon-btn database" [disabled]="form.invalid">Save</button>
|
||||||
|
<div id="spinner"></div>
|
||||||
|
</div>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,92 @@
|
||||||
|
import { Component, EventEmitter, OnInit, Output } from "@angular/core";
|
||||||
|
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
|
||||||
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
import { WizardService } from "../services/wizard.service";
|
||||||
|
import { NotificationService } from "app/services";
|
||||||
|
import { MatTabChangeEvent } from "@angular/material/tabs";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "./database.component.html",
|
||||||
|
styleUrls: ["../welcome/welcome.component.scss"],
|
||||||
|
selector: "wizard-database-selector",
|
||||||
|
})
|
||||||
|
export class DatabaseComponent implements OnInit {
|
||||||
|
public constructor(private fb: FormBuilder, private service: WizardService, private notification: NotificationService) { }
|
||||||
|
@Output() public configuredDatabase = new EventEmitter<void>();
|
||||||
|
|
||||||
|
public form: FormGroup;
|
||||||
|
|
||||||
|
public connectionString = new BehaviorSubject<string>("Server=;Port=3306;Database=ombi");
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.form = this.fb.group({
|
||||||
|
type: [""],
|
||||||
|
host: ["", [Validators.required]],
|
||||||
|
port: [3306, [Validators.required]],
|
||||||
|
name: ["ombi", [Validators.required]],
|
||||||
|
user: [""],
|
||||||
|
password: [""],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.form.valueChanges.subscribe(x => {
|
||||||
|
console.log(x);
|
||||||
|
let connection = `Server=${x.host};Port=${x.port};Database=${x.name}`;
|
||||||
|
|
||||||
|
if (x.user) {
|
||||||
|
connection += `;User=${x.user}`;
|
||||||
|
if (x.password) {
|
||||||
|
connection += `;Password=*******`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.type !== "MySQL") {
|
||||||
|
connection = connection.replace("Server", "Host").replace("User", "Username");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connectionString.next(connection);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public tabChange(event: MatTabChangeEvent) {
|
||||||
|
if (event.index === 0) {
|
||||||
|
this.form.reset();
|
||||||
|
}
|
||||||
|
if (event.index === 1) {
|
||||||
|
this.form.reset({
|
||||||
|
type: "MySQL",
|
||||||
|
host: "",
|
||||||
|
name: "ombi",
|
||||||
|
port: 3306,
|
||||||
|
});
|
||||||
|
this.form.controls.type.setValue("MySQL");
|
||||||
|
|
||||||
|
}
|
||||||
|
if (event.index === 2) {
|
||||||
|
this.form.reset({
|
||||||
|
type:"Postgres",
|
||||||
|
host: "",
|
||||||
|
name: "ombi",
|
||||||
|
port: 5432,
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
this.form.markAllAsTouched();
|
||||||
|
}
|
||||||
|
|
||||||
|
public save() {
|
||||||
|
this.service.addDatabaseConfig(this.form.value).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.notification.success(`Database configuration updated! Please now restart Ombi!`);
|
||||||
|
this.configuredDatabase.emit();
|
||||||
|
},
|
||||||
|
error: error => {
|
||||||
|
if (error.error.message) {
|
||||||
|
this.notification.error(error.error.message);
|
||||||
|
} else {
|
||||||
|
this.notification.error("Something went wrong, please check the logs");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
13
src/Ombi/ClientApp/src/app/wizard/models/DatabaseSettings.ts
Normal file
13
src/Ombi/ClientApp/src/app/wizard/models/DatabaseSettings.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export interface DatabaseSettings {
|
||||||
|
type: string;
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
name: string;
|
||||||
|
user: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DatabaseConfigurationResult {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import { Observable } from "rxjs";
|
||||||
import { ICustomizationSettings } from "../../interfaces";
|
import { ICustomizationSettings } from "../../interfaces";
|
||||||
import { ServiceHelpers } from "../../services";
|
import { ServiceHelpers } from "../../services";
|
||||||
import { IOmbiConfigModel } from "../models/OmbiConfigModel";
|
import { IOmbiConfigModel } from "../models/OmbiConfigModel";
|
||||||
|
import { DatabaseConfigurationResult, DatabaseSettings } from "../models/DatabaseSettings";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -16,4 +17,8 @@ export class WizardService extends ServiceHelpers {
|
||||||
public addOmbiConfig(config: IOmbiConfigModel): Observable<ICustomizationSettings> {
|
public addOmbiConfig(config: IOmbiConfigModel): Observable<ICustomizationSettings> {
|
||||||
return this.http.post<ICustomizationSettings>(`${this.url}config`, config, {headers: this.headers});
|
return this.http.post<ICustomizationSettings>(`${this.url}config`, config, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addDatabaseConfig(config: DatabaseSettings): Observable<DatabaseConfigurationResult> {
|
||||||
|
return this.http.post<DatabaseConfigurationResult>(`${this.url}database`, config, {headers: this.headers});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<div class="wizard-background">
|
<div class="wizard-background">
|
||||||
<div class="container wizard-inner">
|
<div class="container wizard-inner">
|
||||||
<mat-stepper linear #stepper>
|
@if (!needsRestart) {
|
||||||
|
<mat-stepper linear #stepper>
|
||||||
<mat-step >
|
<mat-step >
|
||||||
<form >
|
<form >
|
||||||
<ng-template matStepLabel>Welcome</ng-template>
|
<ng-template matStepLabel>Welcome</ng-template>
|
||||||
|
@ -29,6 +30,12 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
|
<mat-step>
|
||||||
|
<ng-template matStepLabel>Database</ng-template>
|
||||||
|
<wizard-database-selector (configuredDatabase)="databaseConfigured()"></wizard-database-selector>
|
||||||
|
<button mat-button matStepperPrevious class="mat-raised-button mat-error left">Back</button>
|
||||||
|
<button mat-button matStepperNext class="mat-raised-button mat-accent right" data-test="nextDatabase">Next</button>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
<mat-step [optional]="true">
|
<mat-step [optional]="true">
|
||||||
<form >
|
<form >
|
||||||
|
@ -82,5 +89,22 @@
|
||||||
</div>
|
</div>
|
||||||
</mat-step>
|
</mat-step>
|
||||||
</mat-stepper>
|
</mat-stepper>
|
||||||
|
} @else {
|
||||||
|
<mat-stepper linear>
|
||||||
|
<mat-step >
|
||||||
|
<ng-template matStepLabel>Restart</ng-template>
|
||||||
|
<div class="welcome-container">
|
||||||
|
<div class="left-container mediaserver">
|
||||||
|
<i class="fa fa-database text-logo"></i>
|
||||||
|
</div>
|
||||||
|
<div class="right-container">
|
||||||
|
<div class="right-container-content">
|
||||||
|
<h1>Please Restart Ombi for the database changes to take effect!</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
</mat-stepper>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -151,6 +151,12 @@ p.space-or{
|
||||||
color: #A45FC4;
|
color: #A45FC4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.viewon-btn.database {
|
||||||
|
border: 1px solid #A45FC4;
|
||||||
|
color: #A45FC4;
|
||||||
|
}
|
||||||
|
|
||||||
.text-logo{
|
.text-logo{
|
||||||
font-size:12em;
|
font-size:12em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ export class WelcomeComponent implements OnInit {
|
||||||
|
|
||||||
@ViewChild('stepper', {static: false}) public stepper: MatStepper;
|
@ViewChild('stepper', {static: false}) public stepper: MatStepper;
|
||||||
public localUser: ICreateWizardUser;
|
public localUser: ICreateWizardUser;
|
||||||
|
public needsRestart: boolean = false;
|
||||||
public config: IOmbiConfigModel;
|
public config: IOmbiConfigModel;
|
||||||
|
|
||||||
constructor(private router: Router, private identityService: IdentityService,
|
constructor(private router: Router, private identityService: IdentityService,
|
||||||
|
@ -48,7 +49,7 @@ export class WelcomeComponent implements OnInit {
|
||||||
this.settingsService.verifyUrl(this.config.applicationUrl).subscribe(x => {
|
this.settingsService.verifyUrl(this.config.applicationUrl).subscribe(x => {
|
||||||
if (!x) {
|
if (!x) {
|
||||||
this.notificationService.error(`The URL "${this.config.applicationUrl}" is not valid. Please format it correctly e.g. http://www.google.com/`);
|
this.notificationService.error(`The URL "${this.config.applicationUrl}" is not valid. Please format it correctly e.g. http://www.google.com/`);
|
||||||
this.stepper.selectedIndex = 3;
|
this.stepper.selectedIndex = 4;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.saveConfig();
|
this.saveConfig();
|
||||||
|
@ -58,6 +59,10 @@ export class WelcomeComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public databaseConfigured() {
|
||||||
|
this.needsRestart = true;
|
||||||
|
}
|
||||||
|
|
||||||
private saveConfig() {
|
private saveConfig() {
|
||||||
this.WizardService.addOmbiConfig(this.config).subscribe({
|
this.WizardService.addOmbiConfig(this.config).subscribe({
|
||||||
next: (config) => {
|
next: (config) => {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { MediaServerComponent } from "./mediaserver/mediaserver.component";
|
||||||
import { PlexComponent } from "./plex/plex.component";
|
import { PlexComponent } from "./plex/plex.component";
|
||||||
import { WelcomeComponent } from "./welcome/welcome.component";
|
import { WelcomeComponent } from "./welcome/welcome.component";
|
||||||
import { OmbiConfigComponent } from "./ombiconfig/ombiconfig.component";
|
import { OmbiConfigComponent } from "./ombiconfig/ombiconfig.component";
|
||||||
|
import { DatabaseComponent } from "./database/database.component";
|
||||||
|
|
||||||
import { EmbyService } from "../services";
|
import { EmbyService } from "../services";
|
||||||
import { JellyfinService } from "../services";
|
import { JellyfinService } from "../services";
|
||||||
|
@ -48,6 +49,7 @@ const routes: Routes = [
|
||||||
EmbyComponent,
|
EmbyComponent,
|
||||||
JellyfinComponent,
|
JellyfinComponent,
|
||||||
OmbiConfigComponent,
|
OmbiConfigComponent,
|
||||||
|
DatabaseComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
RouterModule,
|
RouterModule,
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Ombi.Attributes;
|
using Ombi.Attributes;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
|
@ -6,6 +8,10 @@ using Ombi.Helpers;
|
||||||
using Ombi.Models.V2;
|
using Ombi.Models.V2;
|
||||||
using Ombi.Settings.Settings.Models;
|
using Ombi.Settings.Settings.Models;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MySqlConnector;
|
||||||
|
using Npgsql;
|
||||||
|
using Ombi.Core.Services;
|
||||||
|
|
||||||
namespace Ombi.Controllers.V2
|
namespace Ombi.Controllers.V2
|
||||||
{
|
{
|
||||||
|
@ -13,15 +19,25 @@ namespace Ombi.Controllers.V2
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public class WizardController : V2Controller
|
public class WizardController : V2Controller
|
||||||
{
|
{
|
||||||
|
private readonly ISettingsService<OmbiSettings> _ombiSettings;
|
||||||
|
private readonly IDatabaseConfigurationService _databaseConfigurationService;
|
||||||
|
private readonly ILogger _logger;
|
||||||
private ISettingsService<CustomizationSettings> _customizationSettings { get; }
|
private ISettingsService<CustomizationSettings> _customizationSettings { get; }
|
||||||
|
|
||||||
public WizardController(ISettingsService<CustomizationSettings> customizationSettings)
|
public WizardController(
|
||||||
|
ISettingsService<CustomizationSettings> customizationSettings,
|
||||||
|
ISettingsService<OmbiSettings> ombiSettings,
|
||||||
|
IDatabaseConfigurationService databaseConfigurationService,
|
||||||
|
ILogger<WizardController> logger)
|
||||||
{
|
{
|
||||||
|
_ombiSettings = ombiSettings;
|
||||||
|
_databaseConfigurationService = databaseConfigurationService;
|
||||||
|
_logger = logger;
|
||||||
_customizationSettings = customizationSettings;
|
_customizationSettings = customizationSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("config")]
|
[HttpPost("config")]
|
||||||
[ApiExplorerSettings(IgnoreApi =true)]
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public async Task<IActionResult> OmbiConfig([FromBody] OmbiConfigModel config)
|
public async Task<IActionResult> OmbiConfig([FromBody] OmbiConfigModel config)
|
||||||
{
|
{
|
||||||
if (config == null)
|
if (config == null)
|
||||||
|
@ -29,6 +45,13 @@ namespace Ombi.Controllers.V2
|
||||||
return BadRequest();
|
return BadRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ombiSettings = await _ombiSettings.GetSettingsAsync();
|
||||||
|
if (ombiSettings.Wizard)
|
||||||
|
{
|
||||||
|
_logger.LogError("Wizard has already been completed");
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
var settings = await _customizationSettings.GetSettingsAsync();
|
var settings = await _customizationSettings.GetSettingsAsync();
|
||||||
|
|
||||||
if (config.ApplicationName.HasValue())
|
if (config.ApplicationName.HasValue())
|
||||||
|
@ -50,5 +73,67 @@ namespace Ombi.Controllers.V2
|
||||||
|
|
||||||
return new OkObjectResult(settings);
|
return new OkObjectResult(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("database")]
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
public async Task<IActionResult> DatabaseConfig([FromBody] WizardDatabaseConfiguration config, CancellationToken token)
|
||||||
|
{
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
var ombiSettings = await _ombiSettings.GetSettingsAsync();
|
||||||
|
if (ombiSettings.Wizard)
|
||||||
|
{
|
||||||
|
_logger.LogError("Wizard has already been completed");
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
var sanitizedType = config.Type.Replace(Environment.NewLine, "").Replace("\n", "").Replace("\r", "");
|
||||||
|
_logger.LogInformation("Setting up database type: {0}", sanitizedType);
|
||||||
|
|
||||||
|
var connectionString = string.Empty;
|
||||||
|
if (config.Type == IDatabaseConfigurationService.MySqlDatabase)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Building MySQL connectionstring");
|
||||||
|
var builder = new MySqlConnectionStringBuilder
|
||||||
|
{
|
||||||
|
Database = config.Name,
|
||||||
|
Port = Convert.ToUInt32(config.Port),
|
||||||
|
Server = config.Host,
|
||||||
|
UserID = config.User,
|
||||||
|
Password = config.Password
|
||||||
|
};
|
||||||
|
|
||||||
|
connectionString = builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.Type == IDatabaseConfigurationService.PostgresDatabase)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Building Postgres connectionstring");
|
||||||
|
var builder = new NpgsqlConnectionStringBuilder
|
||||||
|
{
|
||||||
|
Host = config.Host,
|
||||||
|
Port = config.Port,
|
||||||
|
Database = config.Name,
|
||||||
|
Username = config.User,
|
||||||
|
Password = config.Password
|
||||||
|
};
|
||||||
|
connectionString = builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _databaseConfigurationService.ConfigureDatabase(config.Type, connectionString, token);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return BadRequest(new DatabaseConfigurationResult(false, "Could not configure the database, please check the logs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(new DatabaseConfigurationResult(true, "Database configured successfully"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public record DatabaseConfigurationResult(bool Success, string Message);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||||
using MySqlConnector;
|
using MySqlConnector;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
|
||||||
|
using Ombi.Core.Helpers;
|
||||||
|
using Ombi.Core.Models;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Store.Context;
|
using Ombi.Store.Context;
|
||||||
using Ombi.Store.Context.MySql;
|
using Ombi.Store.Context.MySql;
|
||||||
|
@ -38,11 +40,11 @@ namespace Ombi.Extensions
|
||||||
AddSqliteHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase);
|
AddSqliteHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase);
|
||||||
break;
|
break;
|
||||||
case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase):
|
case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase):
|
||||||
services.AddDbContext<OmbiContext, OmbiMySqlContext>(x => ConfigureMySql(x, configuration.OmbiDatabase));
|
services.AddDbContext<OmbiContext, OmbiMySqlContext>(x => DatabaseConfigurationSetup.ConfigureMySql(x, configuration.OmbiDatabase));
|
||||||
AddMySqlHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase);
|
AddMySqlHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase);
|
||||||
break;
|
break;
|
||||||
case var type when type.Equals(PostgresDatabase, StringComparison.InvariantCultureIgnoreCase):
|
case var type when type.Equals(PostgresDatabase, StringComparison.InvariantCultureIgnoreCase):
|
||||||
services.AddDbContext<OmbiContext, OmbiPostgresContext>(x => ConfigurePostgres(x, configuration.OmbiDatabase));
|
services.AddDbContext<OmbiContext, OmbiPostgresContext>(x => DatabaseConfigurationSetup.ConfigurePostgres(x, configuration.OmbiDatabase));
|
||||||
AddPostgresHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase);
|
AddPostgresHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -54,11 +56,11 @@ namespace Ombi.Extensions
|
||||||
AddSqliteHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase);
|
AddSqliteHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase);
|
||||||
break;
|
break;
|
||||||
case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase):
|
case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase):
|
||||||
services.AddDbContext<ExternalContext, ExternalMySqlContext>(x => ConfigureMySql(x, configuration.ExternalDatabase));
|
services.AddDbContext<ExternalContext, ExternalMySqlContext>(x => DatabaseConfigurationSetup.ConfigureMySql(x, configuration.ExternalDatabase));
|
||||||
AddMySqlHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase);
|
AddMySqlHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase);
|
||||||
break;
|
break;
|
||||||
case var type when type.Equals(PostgresDatabase, StringComparison.InvariantCultureIgnoreCase):
|
case var type when type.Equals(PostgresDatabase, StringComparison.InvariantCultureIgnoreCase):
|
||||||
services.AddDbContext<ExternalContext, ExternalPostgresContext>(x => ConfigurePostgres(x, configuration.ExternalDatabase));
|
services.AddDbContext<ExternalContext, ExternalPostgresContext>(x => DatabaseConfigurationSetup.ConfigurePostgres(x, configuration.ExternalDatabase));
|
||||||
AddPostgresHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase);
|
AddPostgresHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -70,11 +72,11 @@ namespace Ombi.Extensions
|
||||||
AddSqliteHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase);
|
AddSqliteHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase);
|
||||||
break;
|
break;
|
||||||
case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase):
|
case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase):
|
||||||
services.AddDbContext<SettingsContext, SettingsMySqlContext>(x => ConfigureMySql(x, configuration.SettingsDatabase));
|
services.AddDbContext<SettingsContext, SettingsMySqlContext>(x => DatabaseConfigurationSetup.ConfigureMySql(x, configuration.SettingsDatabase));
|
||||||
AddMySqlHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase);
|
AddMySqlHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase);
|
||||||
break;
|
break;
|
||||||
case var type when type.Equals(PostgresDatabase, StringComparison.InvariantCultureIgnoreCase):
|
case var type when type.Equals(PostgresDatabase, StringComparison.InvariantCultureIgnoreCase):
|
||||||
services.AddDbContext<SettingsContext, SettingsPostgresContext>(x => ConfigurePostgres(x, configuration.SettingsDatabase));
|
services.AddDbContext<SettingsContext, SettingsPostgresContext>(x => DatabaseConfigurationSetup.ConfigurePostgres(x, configuration.SettingsDatabase));
|
||||||
AddPostgresHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase);
|
AddPostgresHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -150,95 +152,5 @@ namespace Ombi.Extensions
|
||||||
SQLitePCL.raw.sqlite3_config(raw.SQLITE_CONFIG_MULTITHREAD);
|
SQLitePCL.raw.sqlite3_config(raw.SQLITE_CONFIG_MULTITHREAD);
|
||||||
options.UseSqlite(config.ConnectionString);
|
options.UseSqlite(config.ConnectionString);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ConfigureMySql(DbContextOptionsBuilder options, PerDatabaseConfiguration config)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(config.ConnectionString))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("ConnectionString for the MySql/Mariadb database is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
options.UseMySql(config.ConnectionString, GetServerVersion(config.ConnectionString), b =>
|
|
||||||
{
|
|
||||||
//b.CharSetBehavior(Pomelo.EntityFrameworkCore.MySql.Infrastructure.CharSetBehavior.NeverAppend); // ##ISSUE, link to migrations?
|
|
||||||
b.EnableRetryOnFailure();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ConfigurePostgres(DbContextOptionsBuilder options, PerDatabaseConfiguration config)
|
|
||||||
{
|
|
||||||
options.UseNpgsql(config.ConnectionString, b =>
|
|
||||||
{
|
|
||||||
b.EnableRetryOnFailure();
|
|
||||||
}).ReplaceService<ISqlGenerationHelper, NpgsqlCaseInsensitiveSqlGenerationHelper>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ServerVersion GetServerVersion(string connectionString)
|
|
||||||
{
|
|
||||||
// Workaround Windows bug, that can lead to the following exception:
|
|
||||||
//
|
|
||||||
// MySqlConnector.MySqlException (0x80004005): SSL Authentication Error
|
|
||||||
// ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
|
|
||||||
// ---> System.ComponentModel.Win32Exception (0x8009030F): The message or signature supplied for verification has been altered
|
|
||||||
//
|
|
||||||
// See https://github.com/dotnet/runtime/issues/17005#issuecomment-305848835
|
|
||||||
//
|
|
||||||
// Also workaround for the fact, that ServerVersion.AutoDetect() does not use any retrying strategy.
|
|
||||||
ServerVersion serverVersion = null;
|
|
||||||
#pragma warning disable EF1001
|
|
||||||
var retryPolicy = Policy.Handle<Exception>(exception => MySqlTransientExceptionDetector.ShouldRetryOn(exception))
|
|
||||||
#pragma warning restore EF1001
|
|
||||||
.WaitAndRetry(3, (count, context) => TimeSpan.FromMilliseconds(count * 250));
|
|
||||||
|
|
||||||
serverVersion = retryPolicy.Execute(() => serverVersion = ServerVersion.AutoDetect(connectionString));
|
|
||||||
|
|
||||||
return serverVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DatabaseConfiguration
|
|
||||||
{
|
|
||||||
public DatabaseConfiguration()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public DatabaseConfiguration(string defaultSqlitePath)
|
|
||||||
{
|
|
||||||
OmbiDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "Ombi.db")}");
|
|
||||||
SettingsDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "OmbiSettings.db")}");
|
|
||||||
ExternalDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "OmbiExternal.db")}");
|
|
||||||
}
|
|
||||||
public PerDatabaseConfiguration OmbiDatabase { get; set; }
|
|
||||||
public PerDatabaseConfiguration SettingsDatabase { get; set; }
|
|
||||||
public PerDatabaseConfiguration ExternalDatabase { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PerDatabaseConfiguration
|
|
||||||
{
|
|
||||||
public PerDatabaseConfiguration(string type, string connectionString)
|
|
||||||
{
|
|
||||||
Type = type;
|
|
||||||
ConnectionString = connectionString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used in Deserialization
|
|
||||||
public PerDatabaseConfiguration()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
public string Type { get; set; }
|
|
||||||
public string ConnectionString { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NpgsqlCaseInsensitiveSqlGenerationHelper : NpgsqlSqlGenerationHelper
|
|
||||||
{
|
|
||||||
const string EFMigrationsHisory = "__EFMigrationsHistory";
|
|
||||||
public NpgsqlCaseInsensitiveSqlGenerationHelper(RelationalSqlGenerationHelperDependencies dependencies)
|
|
||||||
: base(dependencies) { }
|
|
||||||
public override string DelimitIdentifier(string identifier) =>
|
|
||||||
base.DelimitIdentifier(identifier == EFMigrationsHisory ? identifier : identifier.ToLower());
|
|
||||||
public override void DelimitIdentifier(StringBuilder builder, string identifier)
|
|
||||||
=> base.DelimitIdentifier(builder, identifier == EFMigrationsHisory ? identifier : identifier.ToLower());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3
src/Ombi/Models/V2/WizardDatabaseConfiguration.cs
Normal file
3
src/Ombi/Models/V2/WizardDatabaseConfiguration.cs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
namespace Ombi.Models.V2;
|
||||||
|
|
||||||
|
public record WizardDatabaseConfiguration(string Type, string Host, int Port, string Name, string User, string Password);
|
|
@ -54,10 +54,6 @@
|
||||||
<Content Remove="database.json" />
|
<Content Remove="database.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="database.json" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -11,6 +11,7 @@ When("I visit Ombi", () => {
|
||||||
|
|
||||||
When("I click through all of the pages", () => {
|
When("I click through all of the pages", () => {
|
||||||
Page.welcomeTab.next.click();
|
Page.welcomeTab.next.click();
|
||||||
|
Page.databaseTab.next.click();
|
||||||
Page.mediaServerTab.next.click();
|
Page.mediaServerTab.next.click();
|
||||||
Page.localUserTab.next.click();
|
Page.localUserTab.next.click();
|
||||||
Page.ombiConfigTab.next.click();
|
Page.ombiConfigTab.next.click();
|
||||||
|
@ -22,6 +23,7 @@ When("I click through all of the pages", () => {
|
||||||
|
|
||||||
When("I click through to the user page", () => {
|
When("I click through to the user page", () => {
|
||||||
Page.welcomeTab.next.click();
|
Page.welcomeTab.next.click();
|
||||||
|
Page.databaseTab.next.click();
|
||||||
Page.mediaServerTab.next.click();
|
Page.mediaServerTab.next.click();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -48,6 +50,6 @@ Then("I should get a notification {string}", (string) => {
|
||||||
|
|
||||||
Then("I should be on the User tab", () => {
|
Then("I should be on the User tab", () => {
|
||||||
Page.matStepsHeader.then((_) => {
|
Page.matStepsHeader.then((_) => {
|
||||||
cy.get('#cdk-step-label-0-2').should('have.attr', 'aria-selected', 'true');
|
cy.get('#cdk-step-label-0-3').should('have.attr', 'aria-selected', 'true');
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -25,7 +25,7 @@
|
||||||
{
|
{
|
||||||
"episodeNumber": 1,
|
"episodeNumber": 1,
|
||||||
"title": "Our Cup Runneth Over",
|
"title": "Our Cup Runneth Over",
|
||||||
"airDate": "2015-01-13T00:00:00",
|
"airDate": "2015-01-13T00:00:00Z",
|
||||||
"url": "https://www.tvmaze.com/episodes/153107/schitts-creek-1x01-our-cup-runneth-over",
|
"url": "https://www.tvmaze.com/episodes/153107/schitts-creek-1x01-our-cup-runneth-over",
|
||||||
"available": false,
|
"available": false,
|
||||||
"approved": false,
|
"approved": false,
|
||||||
|
|
|
@ -20,6 +20,12 @@ class WelcomeTab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DatabaseTab {
|
||||||
|
get next(): Cypress.Chainable<any> {
|
||||||
|
return cy.getByData('nextDatabase');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MediaServerTab {
|
class MediaServerTab {
|
||||||
get next(): Cypress.Chainable<any> {
|
get next(): Cypress.Chainable<any> {
|
||||||
return cy.getByData('nextMediaServer');
|
return cy.getByData('nextMediaServer');
|
||||||
|
@ -35,6 +41,7 @@ class OmbiConfigTab {
|
||||||
|
|
||||||
class WizardPage extends BasePage {
|
class WizardPage extends BasePage {
|
||||||
|
|
||||||
|
databaseTab: DatabaseTab;
|
||||||
localUserTab: LocalUserTab;
|
localUserTab: LocalUserTab;
|
||||||
welcomeTab: WelcomeTab;
|
welcomeTab: WelcomeTab;
|
||||||
mediaServerTab: MediaServerTab;
|
mediaServerTab: MediaServerTab;
|
||||||
|
@ -54,6 +61,7 @@ class WizardPage extends BasePage {
|
||||||
this.welcomeTab = new WelcomeTab();
|
this.welcomeTab = new WelcomeTab();
|
||||||
this.mediaServerTab = new MediaServerTab();
|
this.mediaServerTab = new MediaServerTab();
|
||||||
this.ombiConfigTab = new OmbiConfigTab();
|
this.ombiConfigTab = new OmbiConfigTab();
|
||||||
|
this.databaseTab = new DatabaseTab();
|
||||||
}
|
}
|
||||||
|
|
||||||
visit(options: Cypress.VisitOptions): Cypress.Chainable<Cypress.AUTWindow>;
|
visit(options: Cypress.VisitOptions): Cypress.Chainable<Cypress.AUTWindow>;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue