Compare commits

..

35 Commits

Author SHA1 Message Date
Michael Green
7b241ee13e Miscellaneous bug fixes (#220) 2023-12-09 15:50:39 +11:00
Michael Green
84017639eb Logs now have filtering options (#219)
* Added logging configuration options

* Add support for filtering logs
2023-12-09 14:07:08 +11:00
Michael Green
9e346910f4 Set paging default to "paged" (#218) 2023-12-07 13:57:40 +11:00
Michael Green
e7239c428b Library filtering and display enhancements (#214)
* Implement infinite scrolling and paging (selected via preference) (closes #202)
* Display game counts on more filter types (closes #194)
* Make game counts larger (closes #194)
* Include age groups in filtering (closes #200)
* Add sorting options (closes #145)
2023-12-07 13:20:53 +11:00
Michael Green
9288eb8f12 Enhanced firmware availability page (#213) 2023-12-01 17:01:16 +11:00
Michael Green
e32e7ad36f Improved handling of password user feedback (#212) 2023-12-01 15:51:00 +11:00
Michael Green
3de551be95 Fix spelling error (#211) 2023-12-01 14:40:32 +11:00
Michael Green
8e3fa4f8d5 Added release notes configuration (#210) 2023-12-01 14:27:20 +11:00
Michael Green
b564edb158 Background task execution intervals are now user configurable (#209)
* Store background task intervals in database

* Background task intervals are now user customisable
2023-12-01 13:28:41 +11:00
Michael Green
0bf2ba5d96 Store background task intervals in database (#207) 2023-11-30 09:35:05 +11:00
Michael Green
688af162f5 Make library scan run once a day rather than every hour (#206) 2023-11-30 09:19:00 +11:00
Michael Green
7e4fccb0c1 Apply indexes to improve signature search (#205) 2023-11-30 07:50:32 +11:00
Michael Green
fcdc5cdbde Fixed typo in rematcher code (#192)
* Include ROM's without a signature match in rematcher efforts

* Rematcher will now execute on all titles if manually started

* Fixed typo in rematcher code
2023-11-26 02:06:27 +11:00
Michael Green
f85f246a26 Include ROM's without a signature match in rematcher efforts (#191)
* Include ROM's without a signature match in rematcher efforts

* Rematcher will now execute on all titles if manually started
2023-11-26 01:51:03 +11:00
Michael Green
006f337cb3 Applied correct database query casing (#190) 2023-11-25 23:13:11 +11:00
Michael Green
401a354f04 Resolved platform mapping form save error (#189) 2023-11-25 16:00:05 +11:00
Michael Green
3c25adfc15 Resolved collection build error (#188) 2023-11-25 15:53:46 +11:00
Michael Green
5d0222397f Fixed collection preview load error (#187) 2023-11-25 15:31:13 +11:00
dependabot[bot]
5840d02265 chore(deps): bump gaseous-server/wwwroot/emulators/EmulatorJS (#180)
Bumps [gaseous-server/wwwroot/emulators/EmulatorJS](https://github.com/EmulatorJS/EmulatorJS) from `7c22d03` to `1b3a17f`.
- [Release notes](https://github.com/EmulatorJS/EmulatorJS/releases)
- [Commits](7c22d03b16...1b3a17f6f1)

---
updated-dependencies:
- dependency-name: gaseous-server/wwwroot/emulators/EmulatorJS
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-25 15:04:15 +11:00
dependabot[bot]
97d3a65131 chore(deps): bump MySqlConnector from 2.2.7 to 2.3.1 (#182)
Bumps [MySqlConnector](https://github.com/mysql-net/MySqlConnector) from 2.2.7 to 2.3.1.
- [Release notes](https://github.com/mysql-net/MySqlConnector/releases)
- [Changelog](https://github.com/mysql-net/MySqlConnector/blob/master/docs/VersionHistory.md)
- [Commits](https://github.com/mysql-net/MySqlConnector/compare/2.2.7...2.3.1)

---
updated-dependencies:
- dependency-name: MySqlConnector
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-25 15:03:35 +11:00
Michael Green
3d2f94681a Add authentication support (#185) 2023-11-25 14:50:44 +11:00
Michael Green
2ade60c551 Code clean up and API versioning (#178)
* Merged tools project into main project

* Applied API versioning
2023-10-31 10:42:15 +11:00
Michael Green
1cc7eb22dc Minor UI changes, and bug fixes (#173) 2023-10-27 19:03:48 +11:00
dependabot[bot]
da654c616d chore(deps): bump Microsoft.AspNetCore.OpenApi from 7.0.11 to 7.0.12 (#160)
Bumps [Microsoft.AspNetCore.OpenApi](https://github.com/dotnet/aspnetcore) from 7.0.11 to 7.0.12.
- [Release notes](https://github.com/dotnet/aspnetcore/releases)
- [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md)
- [Commits](https://github.com/dotnet/aspnetcore/compare/v7.0.11...v7.0.12)

---
updated-dependencies:
- dependency-name: Microsoft.AspNetCore.OpenApi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Michael Green <84688932+michael-j-green@users.noreply.github.com>
2023-10-26 13:57:29 +11:00
Michael Green
b76ee71751 EJS version bump (#172) 2023-10-26 13:51:20 +11:00
Michael Green
c2c2831cda Add m3u support for multi image games (#171)
* Parse media type info

* Completed M3U support
2023-10-26 13:39:34 +11:00
Michael Green
a4e5835e34 Logs now show date time rather than x hours ago (#169) 2023-10-18 23:00:35 +11:00
Michael Green
ac97652683 Added cleanup and database optimisation (#168) 2023-10-18 17:52:46 +11:00
Michael Green
7ae6eb82f0 Split LibraryScan and Rematching into separate background tasks (#166)
* Simplified LibraryScan service and moved rematching to it's own service

* Added rematcher to tasks blocked by OrganiseLibrary
2023-10-18 08:56:02 +11:00
Michael Green
8688e1d5c0 Added a try/catch block when unzipping files to ensure errors are properly logged, and temp files are cleaned up (#165) 2023-10-17 11:22:45 +11:00
Michael Green
ffc8be3c12 Add per platform game counts to the library (#164) 2023-10-17 06:49:26 +11:00
Michael Green
7c504ba8fd Recursively import files in the import directory. Duplicates are now moved to the same path, but in the import errors directory (#159) 2023-10-15 15:45:50 +11:00
Michael Green
a8bf5a9412 Added logging for duplicates uploaded via UI (#158) 2023-10-14 16:13:26 +11:00
Michael Green
a190f31ac5 Duplicate imported files are now sent to a duplicates folder 2023-10-14 15:41:36 +11:00
Michael Green
b0e74a2010 Add MariaDB support (#156)
* Fixed startup db check

* Relation tables are created automatically for IGDB metadata

* Removed JSON dependency from filters

* Removed JSON searches from Game library queries

* Gaseous now runs without error on MariaDB

* Fixed static database name bug

* Updated docker files and README
2023-10-14 14:59:53 +11:00
154 changed files with 10594 additions and 1177 deletions

15
.github/release.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
changelog:
categories:
- title: What's New
labels:
- '*'
exclude:
labels:
- bug
- dependencies
- title: Bug Fixes
labels:
- bug
- title: Dependencies
labels:
- dependencies

View File

@@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 16
VisualStudioVersion = 25.0.1704.4 VisualStudioVersion = 25.0.1704.4
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaseous-tools", "gaseous-tools\gaseous-tools.csproj", "{08FE408A-5EC1-4110-ABD8-D19A1155B8CE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaseous-server", "gaseous-server\gaseous-server.csproj", "{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaseous-server", "gaseous-server\gaseous-server.csproj", "{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{17FA6F12-8532-420C-9489-CB8FDE42137C}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{17FA6F12-8532-420C-9489-CB8FDE42137C}"
@@ -37,10 +35,6 @@ Global
{FFCEC386-033F-4772-A45B-D33579F2E5EE}.Debug|Any CPU.Build.0 = Debug|Any CPU {FFCEC386-033F-4772-A45B-D33579F2E5EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FFCEC386-033F-4772-A45B-D33579F2E5EE}.Release|Any CPU.ActiveCfg = Release|Any CPU {FFCEC386-033F-4772-A45B-D33579F2E5EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FFCEC386-033F-4772-A45B-D33579F2E5EE}.Release|Any CPU.Build.0 = Release|Any CPU {FFCEC386-033F-4772-A45B-D33579F2E5EE}.Release|Any CPU.Build.0 = Release|Any CPU
{08FE408A-5EC1-4110-ABD8-D19A1155B8CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{08FE408A-5EC1-4110-ABD8-D19A1155B8CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{08FE408A-5EC1-4110-ABD8-D19A1155B8CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{08FE408A-5EC1-4110-ABD8-D19A1155B8CE}.Release|Any CPU.Build.0 = Release|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.Build.0 = Debug|Any CPU {A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Release|Any CPU.ActiveCfg = Release|Any CPU {A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Release|Any CPU.ActiveCfg = Release|Any CPU

View File

@@ -16,11 +16,12 @@ If you expose the server to the internet, **you do so at your own risk**.
![Emulator](./screenshots/Emulator.png) ![Emulator](./screenshots/Emulator.png)
## Requirements ## Requirements
* MySQL Server 8+* * MariaDB 11.1.2 or MySQL Server 8+
* These are the database versions Gaseous has been tested and developed against. Your mileage may vary with earlier versions.
* Currently MariaDB is the preferred database server, while MySQL will continue to be supported for existing users (they should be interchangable).
* Note that due to the earlier database schema using MySQL specific features, moving to MariaDB from MySQL will require rebuilding your database from scratch. The "Library Scan" background task can be used to re-import all titles.
* Internet Game Database API Key. See: https://api-docs.igdb.com/#account-creation * Internet Game Database API Key. See: https://api-docs.igdb.com/#account-creation
***Note**: MariaDB is currently not supported as Gaseous uses features present only in MySQL. This is being tracked in https://github.com/gaseous-project/gaseous-server/issues/93
## Third Party Projects ## Third Party Projects
The following projects are used by Gaseous The following projects are used by Gaseous
* https://dotnet.microsoft.com/en-us/apps/aspnet * https://dotnet.microsoft.com/en-us/apps/aspnet
@@ -66,7 +67,7 @@ When Gaseous-Server is started for the first time, it creates a configuration fi
}, },
"LoggingConfiguration": { "LoggingConfiguration": {
"DebugLogging": false, "DebugLogging": false,
"LogFormat": "text" "LogRetention": 7
} }
} }
@@ -75,7 +76,7 @@ When Gaseous-Server is started for the first time, it creates a configuration fi
## Docker ## Docker
### Deploy with the prebuilt Docker image ### Deploy with the prebuilt Docker image
Dockerfile and docker-compose.yml files have been provided to make deployment of the server as easy as possible. Dockerfile and docker-compose.yml files have been provided to make deployment of the server as easy as possible.
1. Download the docker-compose.yml file 1. Download the docker-compose-{database}.yml file for the database type you would like to use.
2. Open the docker-compose.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account 2. Open the docker-compose.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account
3. Run the command ```docker-compose up -d``` 3. Run the command ```docker-compose up -d```
4. Connect to the host on port 5198 4. Connect to the host on port 5198
@@ -85,13 +86,13 @@ Dockerfile and docker-compose-build.yml files have been provided to make deploym
1. Clone the repo with ```git clone https://github.com/gaseous-project/gaseous-server.git``` 1. Clone the repo with ```git clone https://github.com/gaseous-project/gaseous-server.git```
2. Change into the gaseous-server directory 2. Change into the gaseous-server directory
3. Clone the submodules with the command ```git submodule update --init``` 3. Clone the submodules with the command ```git submodule update --init```
4. Open the docker-compose-build.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account 4. Open the docker-compose-{database}-build.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account
5. Run the command ```docker-compose --file docker-compose-build.yml up -d``` 5. Run the command ```docker-compose --file docker-compose-{database}-build.yml up -d```
6. Connect to the host on port 5198 6. Connect to the host on port 5198
## Source ## Source
### Build and deploy ### Build and deploy
1. Install and configure a MySQL instance 1. Install and configure a MariaDB or MySQL instance - this is beyond the scope of this document
2. Install the dotnet 7.0 packages appropriate for your operating system 2. Install the dotnet 7.0 packages appropriate for your operating system
* See: https://learn.microsoft.com/en-us/dotnet/core/install/linux * See: https://learn.microsoft.com/en-us/dotnet/core/install/linux
3. Create a database user with permission to create a databse. Gaseous will create the new database and apply the database schema on it's first startup. 3. Create a database user with permission to create a databse. Gaseous will create the new database and apply the database schema on it's first startup.

View File

@@ -0,0 +1,39 @@
version: '2'
services:
gaseous-server:
container_name: gaseous-server
build:
context: ./
restart: unless-stopped
networks:
- gaseous
depends_on:
- gsdb
ports:
- 5198:80
volumes:
- gs:/root/.gaseous-server
environment:
- dbhost=gsdb
- dbuser=root
- dbpass=gaseous
- igdbclientid=<clientid>
- igdbclientsecret=<clientsecret>
gsdb:
container_name: gsdb
image: mariadb
restart: unless-stopped
networks:
- gaseous
volumes:
- gsdb:/var/lib/mysql
environment:
- MARIADB_ROOT_PASSWORD=gaseous
- MARIADB_USER=gaseous
- MARIADB_PASSWORD=gaseous
networks:
gaseous:
driver: bridge
volumes:
gs:
gsdb:

View File

@@ -0,0 +1,38 @@
version: '2'
services:
gaseous-server:
container_name: gaseous-server
image: gaseousgames/gaseousserver:latest
restart: unless-stopped
networks:
- gaseous
depends_on:
- gsdb
ports:
- 5198:80
volumes:
- gs:/root/.gaseous-server
environment:
- dbhost=gsdb
- dbuser=root
- dbpass=gaseous
- igdbclientid=<clientid>
- igdbclientsecret=<clientsecret>
gsdb:
container_name: gsdb
image: mariadb
restart: unless-stopped
networks:
- gaseous
volumes:
- gsdb:/var/lib/mysql
environment:
- MARIADB_ROOT_PASSWORD=gaseous
- MARIADB_USER=gaseous
- MARIADB_PASSWORD=gaseous
networks:
gaseous:
driver: bridge
volumes:
gs:
gsdb:

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Data;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
namespace Authentication
{
/// <summary>
/// Class that implements the ASP.NET Identity
/// IRole interface
/// </summary>
public class ApplicationRole : IdentityRole
{
}
}

View File

@@ -0,0 +1,16 @@
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
using System;
namespace Authentication
{
/// <summary>
/// Class that implements the ASP.NET Identity
/// IUser interface
/// </summary>
public class ApplicationUser : IdentityUser
{
public SecurityProfileViewModel SecurityProfile { get; set; }
public List<UserPreferenceViewModel> UserPreferences { get; set; }
}
}

View File

@@ -0,0 +1,171 @@
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
using MySqlConnector;
namespace Authentication
{
/// <summary>
/// Class that implements the key ASP.NET Identity role store iterfaces
/// </summary>
public class RoleStore : IQueryableRoleStore<ApplicationRole>
{
private RoleTable roleTable;
public Database Database { get; private set; }
public IQueryable<ApplicationRole> Roles
{
get
{
List<ApplicationRole> roles = roleTable.GetRoles();
return roles.AsQueryable();
}
}
public RoleStore()
{
Database = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
roleTable = new RoleTable(Database);
}
/// <summary>
/// Constructor that takes a MySQLDatabase as argument
/// </summary>
/// <param name="database"></param>
public RoleStore(Database database)
{
Database = database;
roleTable = new RoleTable(database);
}
public Task<IdentityResult> CreateAsync(ApplicationRole role, CancellationToken cancellationToken)
{
if (role == null)
{
throw new ArgumentNullException("role");
}
roleTable.Insert(role);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public Task<IdentityResult> DeleteAsync(ApplicationRole role, CancellationToken cancellationToken)
{
if (role == null)
{
throw new ArgumentNullException("user");
}
roleTable.Delete(role.Id);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public Task<ApplicationRole> FindByIdAsync(string roleId, CancellationToken cancellationToken)
{
ApplicationRole result = roleTable.GetRoleById(roleId) as ApplicationRole;
return Task.FromResult<ApplicationRole>(result);
}
public Task<bool> RoleExistsAsync(string roleId, CancellationToken cancellationToken)
{
ApplicationRole? result = roleTable.GetRoleById(roleId) as ApplicationRole;
if (result == null)
{
return Task.FromResult<bool>(false);
}
else
{
return Task.FromResult<bool>(true);
}
}
public Task<ApplicationRole?> FindByNameAsync(string roleName, CancellationToken cancellationToken)
{
ApplicationRole? result = roleTable.GetRoleByName(roleName) as ApplicationRole;
return Task.FromResult<ApplicationRole?>(result);
}
public Task<IdentityResult> UpdateAsync(ApplicationRole role, CancellationToken cancellationToken)
{
if (role == null)
{
throw new ArgumentNullException("user");
}
roleTable.Update(role);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public void Dispose()
{
if (Database != null)
{
Database = null;
}
}
public Task<string> GetRoleIdAsync(ApplicationRole role, CancellationToken cancellationToken)
{
if (role != null)
{
return Task.FromResult<string>(roleTable.GetRoleId(role.Name));
}
return Task.FromResult<string>(null);
}
public Task<string?> GetRoleNameAsync(ApplicationRole role, CancellationToken cancellationToken)
{
if (role != null)
{
return Task.FromResult<string?>(roleTable.GetRoleName(role.Id));
}
return Task.FromResult<string?>(null);
}
public Task SetRoleNameAsync(ApplicationRole role, string? roleName, CancellationToken cancellationToken)
{
if (role == null)
{
throw new ArgumentNullException("role");
}
role.Name = roleName;
roleTable.Update(role);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public Task<string?> GetNormalizedRoleNameAsync(ApplicationRole role, CancellationToken cancellationToken)
{
if (role != null)
{
return Task.FromResult<string?>(roleTable.GetRoleName(role.Id));
}
return Task.FromResult<string?>(null);
}
public Task SetNormalizedRoleNameAsync(ApplicationRole role, string? normalizedName, CancellationToken cancellationToken)
{
if (role == null)
{
throw new ArgumentNullException("role");
}
role.Name = normalizedName;
roleTable.Update(role);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
}
}

View File

@@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Data;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
namespace Authentication
{
/// <summary>
/// Class that represents the Role table in the MySQL Database
/// </summary>
public class RoleTable
{
private Database _database;
/// <summary>
/// Constructor that takes a MySQLDatabase instance
/// </summary>
/// <param name="database"></param>
public RoleTable(Database database)
{
_database = database;
}
/// <summary>
/// Deltes a role from the Roles table
/// </summary>
/// <param name="roleId">The role Id</param>
/// <returns></returns>
public int Delete(string roleId)
{
string commandText = "Delete from Roles where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@id", roleId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Inserts a new Role in the Roles table
/// </summary>
/// <param name="roleName">The role's name</param>
/// <returns></returns>
public int Insert(ApplicationRole role)
{
string commandText = "Insert into Roles (Id, Name) values (@id, @name)";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@name", role.Name);
parameters.Add("@id", role.Id);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Returns a role name given the roleId
/// </summary>
/// <param name="roleId">The role Id</param>
/// <returns>Role name</returns>
public string? GetRoleName(string roleId)
{
string commandText = "Select Name from Roles where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@id", roleId);
DataTable table = _database.ExecuteCMD(commandText, parameters);
if (table.Rows.Count == 0)
{
return null;
}
else
{
return (string)table.Rows[0][0];
}
}
/// <summary>
/// Returns the role Id given a role name
/// </summary>
/// <param name="roleName">Role's name</param>
/// <returns>Role's Id</returns>
public string? GetRoleId(string roleName)
{
string? roleId = null;
string commandText = "Select Id from Roles where Name = @name";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@name", roleName } };
DataTable result = _database.ExecuteCMD(commandText, parameters);
if (result.Rows.Count > 0)
{
return Convert.ToString(result.Rows[0][0]);
}
return roleId;
}
/// <summary>
/// Gets the ApplicationRole given the role Id
/// </summary>
/// <param name="roleId"></param>
/// <returns></returns>
public ApplicationRole? GetRoleById(string roleId)
{
var roleName = GetRoleName(roleId);
ApplicationRole? role = null;
if(roleName != null)
{
role = new ApplicationRole();
role.Id = roleId;
role.Name = roleName;
role.NormalizedName = roleName.ToUpper();
}
return role;
}
/// <summary>
/// Gets the ApplicationRole given the role name
/// </summary>
/// <param name="roleName"></param>
/// <returns></returns>
public ApplicationRole? GetRoleByName(string roleName)
{
var roleId = GetRoleId(roleName);
ApplicationRole role = null;
if (roleId != null)
{
role = new ApplicationRole();
role.Id = roleId;
role.Name = roleName;
role.NormalizedName = roleName.ToUpper();
}
return role;
}
public int Update(ApplicationRole role)
{
string commandText = "Update Roles set Name = @name where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@id", role.Id);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
public List<ApplicationRole> GetRoles()
{
List<ApplicationRole> roles = new List<ApplicationRole>();
string commandText = "Select Name from Roles";
var rows = _database.ExecuteCMDDict(commandText);
foreach(Dictionary<string, object> row in rows)
{
ApplicationRole role = (ApplicationRole)Activator.CreateInstance(typeof(ApplicationRole));
role.Id = (string)row["Id"];
role.Name = (string)row["Name"];
role.NormalizedName = ((string)row["Name"]).ToUpper();
roles.Add(role);
}
return roles;
}
}
}

View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Security.Claims;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
namespace Authentication
{
/// <summary>
/// Class that represents the UserClaims table in the MySQL Database
/// </summary>
public class UserClaimsTable
{
private Database _database;
/// <summary>
/// Constructor that takes a MySQLDatabase instance
/// </summary>
/// <param name="database"></param>
public UserClaimsTable(Database database)
{
_database = database;
}
/// <summary>
/// Returns a ClaimsIdentity instance given a userId
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public ClaimsIdentity FindByUserId(string userId)
{
ClaimsIdentity claims = new ClaimsIdentity();
string commandText = "Select * from UserClaims where UserId = @userId";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@UserId", userId } };
var rows = _database.ExecuteCMD(commandText, parameters).Rows;
foreach (DataRow row in rows)
{
Claim claim = new Claim((string)row["ClaimType"], (string)row["ClaimValue"]);
claims.AddClaim(claim);
}
return claims;
}
/// <summary>
/// Deletes all claims from a user given a userId
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public int Delete(string userId)
{
string commandText = "Delete from UserClaims where UserId = @userId";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("userId", userId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Inserts a new claim in UserClaims table
/// </summary>
/// <param name="userClaim">User's claim to be added</param>
/// <param name="userId">User's id</param>
/// <returns></returns>
public int Insert(Claim userClaim, string userId)
{
string commandText = "Insert into UserClaims (ClaimValue, ClaimType, UserId) values (@value, @type, @userId)";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("value", userClaim.Value);
parameters.Add("type", userClaim.Type);
parameters.Add("userId", userId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Deletes a claim from a user
/// </summary>
/// <param name="user">The user to have a claim deleted</param>
/// <param name="claim">A claim to be deleted from user</param>
/// <returns></returns>
public int Delete(IdentityUser user, Claim claim)
{
string commandText = "Delete from UserClaims where UserId = @userId and @ClaimValue = @value and ClaimType = @type";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("userId", user.Id);
parameters.Add("value", claim.Value);
parameters.Add("type", claim.Type);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
}
}

View File

@@ -0,0 +1,117 @@
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
using System.Collections.Generic;
using System.Data;
namespace Authentication
{
/// <summary>
/// Class that represents the UserLogins table in the MySQL Database
/// </summary>
public class UserLoginsTable
{
private Database _database;
/// <summary>
/// Constructor that takes a MySQLDatabase instance
/// </summary>
/// <param name="database"></param>
public UserLoginsTable(Database database)
{
_database = database;
}
/// <summary>
/// Deletes a login from a user in the UserLogins table
/// </summary>
/// <param name="user">User to have login deleted</param>
/// <param name="login">Login to be deleted from user</param>
/// <returns></returns>
public int Delete(IdentityUser user, UserLoginInfo login)
{
string commandText = "Delete from UserLogins where UserId = @userId and LoginProvider = @loginProvider and ProviderKey = @providerKey";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("UserId", user.Id);
parameters.Add("loginProvider", login.LoginProvider);
parameters.Add("providerKey", login.ProviderKey);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Deletes all Logins from a user in the UserLogins table
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public int Delete(string userId)
{
string commandText = "Delete from UserLogins where UserId = @userId";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("UserId", userId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Inserts a new login in the UserLogins table
/// </summary>
/// <param name="user">User to have new login added</param>
/// <param name="login">Login to be added</param>
/// <returns></returns>
public int Insert(IdentityUser user, UserLoginInfo login)
{
string commandText = "Insert into UserLogins (LoginProvider, ProviderKey, UserId) values (@loginProvider, @providerKey, @userId)";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("loginProvider", login.LoginProvider);
parameters.Add("providerKey", login.ProviderKey);
parameters.Add("userId", user.Id);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Return a userId given a user's login
/// </summary>
/// <param name="userLogin">The user's login info</param>
/// <returns></returns>
public string? FindUserIdByLogin(UserLoginInfo userLogin)
{
string commandText = "Select UserId from UserLogins where LoginProvider = @loginProvider and ProviderKey = @providerKey";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("loginProvider", userLogin.LoginProvider);
parameters.Add("providerKey", userLogin.ProviderKey);
DataTable table = _database.ExecuteCMD(commandText, parameters);
if (table.Rows.Count == 0)
{
return null;
}
else
{
return (string)table.Rows[0][0];
}
}
/// <summary>
/// Returns a list of user's logins
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public List<UserLoginInfo> FindByUserId(string userId)
{
List<UserLoginInfo> logins = new List<UserLoginInfo>();
string commandText = "Select * from UserLogins where UserId = @userId";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@userId", userId } };
var rows = _database.ExecuteCMD(commandText, parameters).Rows;
foreach (DataRow row in rows)
{
var login = new UserLoginInfo((string)row["LoginProvider"], (string)row["ProviderKey"], (string)row["LoginProvider"]);
logins.Add(login);
}
return logins;
}
}
}

View File

@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Data;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
namespace Authentication
{
/// <summary>
/// Class that represents the UserRoles table in the MySQL Database
/// </summary>
public class UserRolesTable
{
private Database _database;
/// <summary>
/// Constructor that takes a MySQLDatabase instance
/// </summary>
/// <param name="database"></param>
public UserRolesTable(Database database)
{
_database = database;
}
/// <summary>
/// Returns a list of user's roles
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public List<string> FindByUserId(string userId)
{
List<string> roles = new List<string>();
string commandText = "Select Roles.Name from UserRoles, Roles where UserRoles.UserId = @userId and UserRoles.RoleId = Roles.Id";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@userId", userId);
var rows = _database.ExecuteCMD(commandText, parameters).Rows;
foreach(DataRow row in rows)
{
roles.Add((string)row["Name"]);
}
return roles;
}
/// <summary>
/// Deletes all roles from a user in the UserRoles table
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public int Delete(string userId)
{
string commandText = "Delete from UserRoles where UserId = @userId";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("UserId", userId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
public int DeleteUserFromRole(string userId, string roleId)
{
string commandText = "Delete from UserRoles where UserId = @userId and RoleId = @roleId";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("userId", userId);
parameters.Add("roleId", roleId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
/// <summary>
/// Inserts a new role for a user in the UserRoles table
/// </summary>
/// <param name="user">The User</param>
/// <param name="roleId">The Role's id</param>
/// <returns></returns>
public int Insert(IdentityUser user, string roleId)
{
string commandText = "Insert into UserRoles (UserId, RoleId) values (@userId, @roleId)";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("userId", user.Id);
parameters.Add("roleId", roleId);
return (int)_database.ExecuteNonQuery(commandText, parameters);
}
}
}

View File

@@ -0,0 +1,616 @@
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
using MySqlConnector;
namespace Authentication
{
public class UserStore :
IUserStore<ApplicationUser>,
IUserRoleStore<ApplicationUser>,
IUserLoginStore<ApplicationUser>,
IUserClaimStore<ApplicationUser>,
IUserPasswordStore<ApplicationUser>,
IUserSecurityStampStore<ApplicationUser>,
IQueryableUserStore<ApplicationUser>,
IUserEmailStore<ApplicationUser>,
IUserPhoneNumberStore<ApplicationUser>,
IUserTwoFactorStore<ApplicationUser>,
IUserLockoutStore<ApplicationUser>
{
private Database database;
private UserTable<ApplicationUser> userTable;
private RoleTable roleTable;
private UserRolesTable userRolesTable;
private UserLoginsTable userLoginsTable;
private UserClaimsTable userClaimsTable;
public UserStore()
{
database = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
userTable = new UserTable<ApplicationUser>(database);
roleTable = new RoleTable(database);
userRolesTable = new UserRolesTable(database);
userLoginsTable = new UserLoginsTable(database);
userClaimsTable = new UserClaimsTable(database);
}
public UserStore(Database database)
{
this.database = database;
userTable = new UserTable<ApplicationUser>(database);
roleTable = new RoleTable(database);
userRolesTable = new UserRolesTable(database);
userLoginsTable = new UserLoginsTable(database);
userClaimsTable = new UserClaimsTable(database);
}
public IQueryable<ApplicationUser> Users
{
get
{
List<ApplicationUser> users = userTable.GetUsers();
return users.AsQueryable();
}
}
public Task AddClaimsAsync(ApplicationUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claims == null)
{
throw new ArgumentNullException("user");
}
foreach (Claim claim in claims)
{
userClaimsTable.Insert(claim, user.Id);
}
return Task.FromResult<object>(null);
}
public Task AddLoginAsync(ApplicationUser user, UserLoginInfo login, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (login == null)
{
throw new ArgumentNullException("login");
}
userLoginsTable.Insert(user, login);
return Task.FromResult<object>(null);
}
public Task AddToRoleAsync(ApplicationUser user, string roleName, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (string.IsNullOrEmpty(roleName))
{
throw new ArgumentException("Argument cannot be null or empty: roleName.");
}
string roleId = roleTable.GetRoleId(roleName);
if (!string.IsNullOrEmpty(roleId))
{
userRolesTable.Insert(user, roleId);
}
return Task.FromResult<object>(null);
}
public Task<IdentityResult> CreateAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
userTable.Insert(user);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public Task<IdentityResult> DeleteAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user != null)
{
userTable.Delete(user);
}
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public void Dispose()
{
if (database != null)
{
database = null;
}
}
public Task<ApplicationUser?> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken)
{
if (String.IsNullOrEmpty(normalizedEmail))
{
throw new ArgumentNullException("email");
}
ApplicationUser result = userTable.GetUserByEmail(normalizedEmail) as ApplicationUser;
if (result != null)
{
return Task.FromResult<ApplicationUser>(result);
}
return Task.FromResult<ApplicationUser>(null);
}
public Task<ApplicationUser?> FindByIdAsync(string userId, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(userId))
{
throw new ArgumentException("Null or empty argument: userId");
}
ApplicationUser result = userTable.GetUserById(userId) as ApplicationUser;
if (result != null)
{
return Task.FromResult<ApplicationUser>(result);
}
return Task.FromResult<ApplicationUser>(null);
}
public Task<ApplicationUser?> FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken)
{
if (loginProvider == null || providerKey == null)
{
throw new ArgumentNullException("login");
}
UserLoginInfo login = new UserLoginInfo(loginProvider, providerKey, loginProvider);
var userId = userLoginsTable.FindUserIdByLogin(login);
if (userId != null)
{
ApplicationUser user = userTable.GetUserById(userId) as ApplicationUser;
if (user != null)
{
return Task.FromResult<ApplicationUser>(user);
}
}
return Task.FromResult<ApplicationUser>(null);
}
public Task<ApplicationUser?> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(normalizedUserName))
{
throw new ArgumentException("Null or empty argument: normalizedUserName");
}
List<ApplicationUser> result = userTable.GetUserByName(normalizedUserName) as List<ApplicationUser>;
// Should I throw if > 1 user?
if (result != null && result.Count == 1)
{
return Task.FromResult<ApplicationUser>(result[0]);
}
return Task.FromResult<ApplicationUser>(null);
}
public Task<int> GetAccessFailedCountAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.AccessFailedCount);
}
public Task<IList<Claim>> GetClaimsAsync(ApplicationUser user, CancellationToken cancellationToken)
{
ClaimsIdentity identity = userClaimsTable.FindByUserId(user.Id);
return Task.FromResult<IList<Claim>>(identity.Claims.ToList());
}
public Task<string?> GetEmailAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.Email);
}
public Task<bool> GetEmailConfirmedAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.EmailConfirmed);
}
public Task<bool> GetLockoutEnabledAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.LockoutEnabled);
}
public Task<DateTimeOffset?> GetLockoutEndDateAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user.LockoutEnd.HasValue)
{
return Task.FromResult((DateTimeOffset?)user.LockoutEnd.Value);
}
else
{
return Task.FromResult((DateTimeOffset?)new DateTimeOffset());
}
}
public Task<IList<UserLoginInfo>> GetLoginsAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
List<UserLoginInfo> logins = userLoginsTable.FindByUserId(user.Id);
if (logins != null)
{
return Task.FromResult<IList<UserLoginInfo>>(logins);
}
return Task.FromResult<IList<UserLoginInfo>>(null);
}
public Task<string?> GetNormalizedEmailAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.NormalizedEmail);
}
public Task<string?> GetNormalizedUserNameAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user != null)
{
return Task.FromResult<string?>(userTable.GetUserName(user.Id));
}
return Task.FromResult<string?>(null);
}
public Task<string?> GetPasswordHashAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user != null)
{
return Task.FromResult<string?>(userTable.GetPasswordHash(user.Id));
}
return Task.FromResult<string?>(null);
}
public Task<string?> GetPhoneNumberAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.PhoneNumber);
}
public Task<bool> GetPhoneNumberConfirmedAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.PhoneNumberConfirmed);
}
public Task<IList<string>> GetRolesAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
List<string> roles = userRolesTable.FindByUserId(user.Id);
{
if (roles != null)
{
return Task.FromResult<IList<string>>(roles);
}
}
return Task.FromResult<IList<string>>(null);
}
public Task<string?> GetSecurityStampAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.SecurityStamp);
}
public Task<bool> GetTwoFactorEnabledAsync(ApplicationUser user, CancellationToken cancellationToken)
{
return Task.FromResult(user.TwoFactorEnabled);
}
public Task<string> GetUserIdAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user != null)
{
return Task.FromResult<string>(userTable.GetUserId(user.NormalizedUserName));
}
return Task.FromResult<string>(null);
}
public Task<string?> GetUserNameAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user != null)
{
//return Task.FromResult<string?>(userTable.GetUserName(user.Id));
return Task.FromResult(user.UserName);
}
return Task.FromResult<string?>(null);
}
public Task<IList<ApplicationUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IList<ApplicationUser>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<bool> HasPasswordAsync(ApplicationUser user, CancellationToken cancellationToken)
{
var hasPassword = !string.IsNullOrEmpty(userTable.GetPasswordHash(user.Id));
return Task.FromResult<bool>(Boolean.Parse(hasPassword.ToString()));
}
public Task<int> IncrementAccessFailedCountAsync(ApplicationUser user, CancellationToken cancellationToken)
{
user.AccessFailedCount++;
userTable.Update(user);
return Task.FromResult(user.AccessFailedCount);
}
public Task<bool> IsInRoleAsync(ApplicationUser user, string roleName, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (string.IsNullOrEmpty(roleName))
{
throw new ArgumentNullException("role");
}
List<string> roles = userRolesTable.FindByUserId(user.Id);
{
if (roles != null)
{
foreach (string role in roles)
{
if (role.ToUpper() == roleName.ToUpper())
{
return Task.FromResult<bool>(true);
}
}
}
}
return Task.FromResult<bool>(false);
}
public Task RemoveClaimsAsync(ApplicationUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claims == null)
{
throw new ArgumentNullException("claim");
}
foreach (Claim claim in claims)
{
userClaimsTable.Delete(user, claim);
}
return Task.FromResult<object>(null);
}
public Task RemoveFromRoleAsync(ApplicationUser user, string roleName, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (roleName == null)
{
throw new ArgumentNullException("role");
}
IdentityRole? role = roleTable.GetRoleByName(roleName);
if (role != null)
{
userRolesTable.DeleteUserFromRole(user.Id, role.Id);
}
return Task.FromResult<Object>(null);
}
public Task RemoveLoginAsync(ApplicationUser user, string loginProvider, string providerKey, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (loginProvider == null || providerKey == null)
{
throw new ArgumentNullException("login");
}
UserLoginInfo login = new UserLoginInfo(loginProvider, providerKey, loginProvider);
userLoginsTable.Delete(user, login);
return Task.FromResult<Object>(null);
}
public Task ReplaceClaimAsync(ApplicationUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claim == null || newClaim == null)
{
throw new ArgumentNullException("claim");
}
userClaimsTable.Delete(user, claim);
userClaimsTable.Insert(newClaim, user.Id);
return Task.FromResult<Object>(null);
}
public Task ResetAccessFailedCountAsync(ApplicationUser user, CancellationToken cancellationToken)
{
user.AccessFailedCount = 0;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetEmailAsync(ApplicationUser user, string? email, CancellationToken cancellationToken)
{
user.Email = email;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetEmailConfirmedAsync(ApplicationUser user, bool confirmed, CancellationToken cancellationToken)
{
user.EmailConfirmed = confirmed;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetLockoutEnabledAsync(ApplicationUser user, bool enabled, CancellationToken cancellationToken)
{
user.LockoutEnabled = enabled;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetLockoutEndDateAsync(ApplicationUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken)
{
user.LockoutEnd = lockoutEnd;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetNormalizedEmailAsync(ApplicationUser user, string? normalizedEmail, CancellationToken cancellationToken)
{
user.NormalizedEmail = normalizedEmail;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetNormalizedUserNameAsync(ApplicationUser user, string? normalizedName, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
user.NormalizedUserName = normalizedName;
userTable.Update(user);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public Task SetPasswordHashAsync(ApplicationUser user, string? passwordHash, CancellationToken cancellationToken)
{
user.PasswordHash = passwordHash;
return Task.FromResult<Object>(null);
}
public Task SetPhoneNumberAsync(ApplicationUser user, string? phoneNumber, CancellationToken cancellationToken)
{
user.PhoneNumber = phoneNumber;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetPhoneNumberConfirmedAsync(ApplicationUser user, bool confirmed, CancellationToken cancellationToken)
{
user.PhoneNumberConfirmed = confirmed;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetSecurityStampAsync(ApplicationUser user, string stamp, CancellationToken cancellationToken)
{
user.SecurityStamp = stamp;
return Task.FromResult(0);
}
public Task SetTwoFactorEnabledAsync(ApplicationUser user, bool enabled, CancellationToken cancellationToken)
{
user.TwoFactorEnabled = enabled;
userTable.Update(user);
return Task.FromResult(0);
}
public Task SetUserNameAsync(ApplicationUser user, string? userName, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
user.UserName = userName;
userTable.Update(user);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
public Task<IdentityResult> UpdateAsync(ApplicationUser user, CancellationToken cancellationToken)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
userTable.Update(user);
return Task.FromResult<IdentityResult>(IdentityResult.Success);
}
}
}

View File

@@ -0,0 +1,441 @@
using System;
using System.Collections.Generic;
using System.Data;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Identity;
namespace Authentication
{
/// <summary>
/// Class that represents the Users table in the MySQL Database
/// </summary>
public class UserTable<TUser>
where TUser :ApplicationUser
{
private Database _database;
/// <summary>
/// Constructor that takes a MySQLDatabase instance
/// </summary>
/// <param name="database"></param>
public UserTable(Database database)
{
_database = database;
}
/// <summary>
/// Returns the user's name given a user id
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public string? GetUserName(string userId)
{
string commandText = "Select NormalizedUserName from Users where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@id", userId } };
DataTable table = _database.ExecuteCMD(commandText, parameters);
if (table.Rows.Count == 0)
{
return null;
}
else
{
return (string)table.Rows[0][0];
}
}
/// <summary>
/// Returns a User ID given a user name
/// </summary>
/// <param name="userName">The user's name</param>
/// <returns></returns>
public string? GetUserId(string normalizedUserName)
{
string commandText = "Select Id from Users where NormalizedUserName = @name";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@name", normalizedUserName } };
DataTable table = _database.ExecuteCMD(commandText, parameters);
if (table.Rows.Count == 0)
{
return null;
}
else
{
return (string)table.Rows[0][0];
}
}
/// <summary>
/// Returns an TUser given the user's id
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public TUser GetUserById(string userId)
{
TUser user = null;
string commandText = "Select * from Users where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@id", userId } };
var rows = _database.ExecuteCMDDict(commandText, parameters);
if (rows != null && rows.Count == 1)
{
Dictionary<string, object> row = rows[0];
user = (TUser)Activator.CreateInstance(typeof(TUser));
user.Id = (string)row["Id"];
user.UserName = (string?)row["UserName"];
user.PasswordHash = (string?)(string.IsNullOrEmpty((string?)row["PasswordHash"]) ? null : row["PasswordHash"]);
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
user.Email = (string?)(string.IsNullOrEmpty((string?)row["Email"]) ? null : row["Email"]);
user.EmailConfirmed = row["EmailConfirmed"] == "1" ? true:false;
user.PhoneNumber = (string?)(string.IsNullOrEmpty((string?)row["PhoneNumber"]) ? null : row["PhoneNumber"]);
user.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
user.NormalizedUserName = (string?)(string.IsNullOrEmpty((string?)row["NormalizedUserName"]) ? null : row["NormalizedUserName"]);
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
user.LockoutEnd = string.IsNullOrEmpty((string?)row["LockoutEnd"]) ? DateTime.Now : DateTime.Parse((string?)row["LockoutEnd"]);
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
user.SecurityProfile = GetSecurityProfile(user);
user.UserPreferences = GetPreferences(user);
}
return user;
}
/// <summary>
/// Returns a list of TUser instances given a user name
/// </summary>
/// <param name="normalizedUserName">User's name</param>
/// <returns></returns>
public List<TUser> GetUserByName(string normalizedUserName)
{
List<TUser> users = new List<TUser>();
string commandText = "Select * from Users where NormalizedEmail = @name";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@name", normalizedUserName } };
var rows = _database.ExecuteCMDDict(commandText, parameters);
foreach(Dictionary<string, object> row in rows)
{
TUser user = (TUser)Activator.CreateInstance(typeof(TUser));
user.Id = (string)row["Id"];
user.UserName = (string?)row["UserName"];
user.PasswordHash = (string?)(string.IsNullOrEmpty((string?)row["PasswordHash"]) ? null : row["PasswordHash"]);
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
user.Email = (string?)(string.IsNullOrEmpty((string?)row["Email"]) ? null : row["Email"]);
user.EmailConfirmed = row["EmailConfirmed"] == "1" ? true:false;
user.PhoneNumber = (string?)(string.IsNullOrEmpty((string?)row["PhoneNumber"]) ? null : row["PhoneNumber"]);
user.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
user.NormalizedUserName = (string?)(string.IsNullOrEmpty((string?)row["NormalizedUserName"]) ? null : row["NormalizedUserName"]);
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
user.LockoutEnd = string.IsNullOrEmpty((string?)row["LockoutEnd"]) ? DateTime.Now : DateTime.Parse((string?)row["LockoutEnd"]);
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
user.SecurityProfile = GetSecurityProfile(user);
user.UserPreferences = GetPreferences(user);
users.Add(user);
}
return users;
}
public List<TUser> GetUsers()
{
List<TUser> users = new List<TUser>();
string commandText = "Select * from Users order by NormalizedUserName";
var rows = _database.ExecuteCMDDict(commandText);
foreach(Dictionary<string, object> row in rows)
{
TUser user = (TUser)Activator.CreateInstance(typeof(TUser));
user.Id = (string)row["Id"];
user.UserName = (string?)row["UserName"];
user.PasswordHash = (string?)(string.IsNullOrEmpty((string?)row["PasswordHash"]) ? null : row["PasswordHash"]);
user.SecurityStamp = (string?)(string.IsNullOrEmpty((string?)row["SecurityStamp"]) ? null : row["SecurityStamp"]);
user.ConcurrencyStamp = (string?)(string.IsNullOrEmpty((string?)row["ConcurrencyStamp"]) ? null : row["ConcurrencyStamp"]);
user.Email = (string?)(string.IsNullOrEmpty((string?)row["Email"]) ? null : row["Email"]);
user.EmailConfirmed = row["EmailConfirmed"] == "1" ? true:false;
user.PhoneNumber = (string?)(string.IsNullOrEmpty((string?)row["PhoneNumber"]) ? null : row["PhoneNumber"]);
user.PhoneNumberConfirmed = row["PhoneNumberConfirmed"] == "1" ? true : false;
user.NormalizedEmail = (string?)(string.IsNullOrEmpty((string?)row["NormalizedEmail"]) ? null : row["NormalizedEmail"]);
user.NormalizedUserName = (string?)(string.IsNullOrEmpty((string?)row["NormalizedUserName"]) ? null : row["NormalizedUserName"]);
user.LockoutEnabled = row["LockoutEnabled"] == "1" ? true : false;
user.LockoutEnd = string.IsNullOrEmpty((string?)row["LockoutEnd"]) ? DateTime.Now : DateTime.Parse((string?)row["LockoutEnd"]);
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
user.SecurityProfile = GetSecurityProfile(user);
user.UserPreferences = GetPreferences(user);
users.Add(user);
}
return users;
}
public TUser GetUserByEmail(string email)
{
List<TUser> users = GetUserByName(email);
if (users.Count == 0)
{
return null;
}
else
{
return users[0];
}
}
/// <summary>
/// Return the user's password hash
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
public string GetPasswordHash(string userId)
{
string commandText = "Select PasswordHash from Users where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@id", userId);
DataTable table = _database.ExecuteCMD(commandText, parameters);
if (table.Rows.Count == 0)
{
return null;
}
else
{
return (string)table.Rows[0][0];
}
}
/// <summary>
/// Sets the user's password hash
/// </summary>
/// <param name="userId"></param>
/// <param name="passwordHash"></param>
/// <returns></returns>
public int SetPasswordHash(string userId, string passwordHash)
{
string commandText = "Update Users set PasswordHash = @pwdHash where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@pwdHash", passwordHash);
parameters.Add("@id", userId);
return _database.ExecuteCMD(commandText, parameters).Rows.Count;
}
/// <summary>
/// Returns the user's security stamp
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public string GetSecurityStamp(string userId)
{
string commandText = "Select SecurityStamp from Users where Id = @id";
Dictionary<string, object> parameters = new Dictionary<string, object>() { { "@id", userId } };
DataTable table = _database.ExecuteCMD(commandText, parameters);
if (table.Rows.Count == 0)
{
return null;
}
else
{
return (string)table.Rows[0][0];
}
}
/// <summary>
/// Inserts a new user in the Users table
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public int Insert(TUser user)
{
string commandText = @"Insert into Users (UserName, Id, PasswordHash, SecurityStamp, ConcurrencyStamp, Email, EmailConfirmed, PhoneNumber, PhoneNumberConfirmed, NormalizedEmail, NormalizedUserName, AccessFailedCount, LockoutEnabled, LockoutEnd, TwoFactorEnabled) values (@name, @id, @pwdHash, @SecStamp, @concurrencystamp, @email ,@emailconfirmed ,@phonenumber, @phonenumberconfirmed, @normalizedemail, @normalizedusername, @accesscount, @lockoutenabled, @lockoutenddate, @twofactorenabled);";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@name", user.UserName);
parameters.Add("@id", user.Id);
parameters.Add("@pwdHash", user.PasswordHash);
parameters.Add("@SecStamp", user.SecurityStamp);
parameters.Add("@concurrencystamp", user.ConcurrencyStamp);
parameters.Add("@email", user.Email);
parameters.Add("@emailconfirmed", user.EmailConfirmed);
parameters.Add("@phonenumber", user.PhoneNumber);
parameters.Add("@phonenumberconfirmed", user.PhoneNumberConfirmed);
parameters.Add("@normalizedemail", user.NormalizedEmail);
parameters.Add("@normalizedusername", user.NormalizedUserName);
parameters.Add("@accesscount", user.AccessFailedCount);
parameters.Add("@lockoutenabled", user.LockoutEnabled);
parameters.Add("@lockoutenddate", user.LockoutEnd);
parameters.Add("@twofactorenabled", user.TwoFactorEnabled);
// set default security profile
SetSecurityProfile(user, new SecurityProfileViewModel());
// set default preferences
SetPreferences(user, new List<UserPreferenceViewModel>());
return _database.ExecuteCMD(commandText, parameters).Rows.Count;
}
/// <summary>
/// Deletes a user from the Users table
/// </summary>
/// <param name="userId">The user's id</param>
/// <returns></returns>
private int Delete(string userId)
{
string commandText = "Delete from Users where Id = @userId; Delete from User_Settings where Id = @userId;";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@userId", userId);
return _database.ExecuteCMD(commandText, parameters).Rows.Count;
}
/// <summary>
/// Deletes a user from the Users table
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public int Delete(TUser user)
{
return Delete(user.Id);
}
/// <summary>
/// Updates a user in the Users table
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public int Update(TUser user)
{
string commandText = @"Update Users set UserName = @userName, PasswordHash = @pwdHash, SecurityStamp = @secStamp, ConcurrencyStamp = @concurrencystamp, Email = @email, EmailConfirmed = @emailconfirmed, PhoneNumber = @phonenumber, PhoneNumberConfirmed = @phonenumberconfirmed, NormalizedEmail = @normalizedemail, NormalizedUserName = @normalizedusername, AccessFailedCount = @accesscount, LockoutEnabled = @lockoutenabled, LockoutEnd = @lockoutenddate, TwoFactorEnabled=@twofactorenabled WHERE Id = @userId;";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@userId", user.Id);
parameters.Add("@userName", user.UserName);
parameters.Add("@pwdHash", user.PasswordHash);
parameters.Add("@SecStamp", user.SecurityStamp);
parameters.Add("@concurrencystamp", user.ConcurrencyStamp);
parameters.Add("@email", user.Email);
parameters.Add("@emailconfirmed", user.EmailConfirmed);
parameters.Add("@phonenumber", user.PhoneNumber);
parameters.Add("@phonenumberconfirmed", user.PhoneNumberConfirmed);
parameters.Add("@normalizedemail", user.NormalizedEmail);
parameters.Add("@normalizedusername", user.NormalizedUserName);
parameters.Add("@accesscount", user.AccessFailedCount);
parameters.Add("@lockoutenabled", user.LockoutEnabled);
parameters.Add("@lockoutenddate", user.LockoutEnd);
parameters.Add("@twofactorenabled", user.TwoFactorEnabled);
// set the security profile
SetSecurityProfile(user, user.SecurityProfile);
// set preferences
SetPreferences(user, user.UserPreferences);
return _database.ExecuteCMD(commandText, parameters).Rows.Count;
}
private SecurityProfileViewModel GetSecurityProfile(TUser user)
{
string sql = "SELECT SecurityProfile FROM Users WHERE Id=@Id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Id", user.Id);
List<Dictionary<string, object>> data = _database.ExecuteCMDDict(sql, dbDict);
if (data.Count == 0)
{
// no saved profile - return the default one
return new SecurityProfileViewModel();
}
else
{
string? securityProfileString = (string?)data[0]["SecurityProfile"];
if (securityProfileString != null && securityProfileString != "null")
{
SecurityProfileViewModel securityProfile = Newtonsoft.Json.JsonConvert.DeserializeObject<SecurityProfileViewModel>(securityProfileString);
return securityProfile;
}
else
{
return new SecurityProfileViewModel();
}
}
}
private int SetSecurityProfile(TUser user, SecurityProfileViewModel securityProfile)
{
string commandText = "UPDATE Users SET SecurityProfile=@SecurityProfile WHERE Id=@Id;";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("Id", user.Id);
parameters.Add("SecurityProfile", Newtonsoft.Json.JsonConvert.SerializeObject(securityProfile));
return _database.ExecuteCMD(commandText, parameters).Rows.Count;
}
public List<UserPreferenceViewModel> GetPreferences(TUser user)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT `Setting`, `Value` FROM User_Settings WHERE Id=@id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", user.Id);
DataTable data = db.ExecuteCMD(sql, dbDict);
List<UserPreferenceViewModel> userPrefs = new List<UserPreferenceViewModel>();
foreach (DataRow row in data.Rows)
{
UserPreferenceViewModel userPref = new UserPreferenceViewModel();
userPref.Setting = (string)row["Setting"];
userPref.Value = (string)row["Value"];
userPrefs.Add(userPref);
}
return userPrefs;
}
public int SetPreferences(TUser user, List<UserPreferenceViewModel> model)
{
if (model != null)
{
List<UserPreferenceViewModel> userPreferences = GetPreferences(user);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
foreach (UserPreferenceViewModel modelItem in model)
{
bool prefItemFound = false;
foreach (UserPreferenceViewModel existing in userPreferences)
{
if (existing.Setting.ToLower() == modelItem.Setting.ToLower())
{
prefItemFound = true;
break;
}
}
string sql = "INSERT INTO User_Settings (`Id`, `Setting`, `Value`) VALUES (@id, @setting, @value);";
if (prefItemFound == true)
{
sql = "UPDATE User_Settings SET `Value`=@value WHERE `Id`=@id AND `Setting`=@setting";
}
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", user.Id);
dbDict.Add("setting", modelItem.Setting);
dbDict.Add("value", modelItem.Value);
db.ExecuteNonQuery(sql, dbDict);
}
return model.Count;
}
else
{
return 0;
}
}
}
}

View File

@@ -0,0 +1,100 @@
using System.ComponentModel.DataAnnotations;
namespace Authentication
{
public class ExternalLoginConfirmationViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
}
public class ManageUserViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class LoginViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
public class RegisterViewModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
public class ForgotPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class AddPhoneNumberViewModel
{
[Required]
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}

View File

@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class ChangePasswordViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 10)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}

View File

@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class DisplayRecoveryCodesViewModel
{
[Required]
public IEnumerable<string> Codes { get; set; }
}

View File

@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.AspNetCore.Identity;
namespace Authentication;
public class IndexViewModel
{
public bool HasPassword { get; set; }
public IList<UserLoginInfo> Logins { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
public string AuthenticatorKey { get; set; }
}

View File

@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
namespace Authentication;
public class ManageLoginsViewModel
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationScheme> OtherLogins { get; set; }
}

View File

@@ -0,0 +1,47 @@
namespace Authentication
{
public class ProfileBasicViewModel
{
public string UserId { get; set; }
public string UserName { get; set; }
public string EmailAddress { get; set; }
public List<String> Roles { get; set; }
public SecurityProfileViewModel SecurityProfile { get; set; }
public List<UserPreferenceViewModel> UserPreferences { get; set; }
public string HighestRole {
get
{
string _highestRole = "";
foreach (string role in Roles)
{
switch (role)
{
case "Admin":
// there is no higher
_highestRole = role;
break;
case "Gamer":
// only one high is Admin, so check for that
if (_highestRole != "Admin")
{
_highestRole = role;
}
break;
case "Player":
// make sure _highestRole isn't already set to Gamer or Admin
if (_highestRole != "Admin" && _highestRole != "Gamer")
{
_highestRole = role;
}
break;
default:
_highestRole = "Player";
break;
}
}
return _highestRole;
}
}
}
}

View File

@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Authentication;
public class RemoveLoginViewModel
{
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
}

View File

@@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
namespace Authentication
{
public class SecurityProfileViewModel
{
public AgeRestrictionItem AgeRestrictionPolicy { get; set; } = new AgeRestrictionItem{
MaximumAgeRestriction = gaseous_server.Classes.Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings.Adult,
IncludeUnrated = true
};
public class AgeRestrictionItem
{
public gaseous_server.Classes.Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction { get; set; }
public bool IncludeUnrated { get; set; }
}
}
}

View File

@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Authentication;
public class SendCodeViewModel
{
public string SelectedProvider { get; set; }
public ICollection<SelectListItem> Providers { get; set; }
public string ReturnUrl { get; set; }
public bool RememberMe { get; set; }
}

View File

@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class SetPasswordViewModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}

View File

@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class UseRecoveryCodeViewModel
{
[Required]
public string Code { get; set; }
public string ReturnUrl { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace Authentication;
public class UserPreferenceViewModel
{
public string Setting { get; set; }
public string Value { get; set; }
}

View File

@@ -0,0 +1,47 @@
namespace Authentication
{
public class UserViewModel
{
public string Id { get; set; }
public string EmailAddress { get; set; }
public bool LockoutEnabled { get; set; }
public DateTimeOffset? LockoutEnd { get; set; }
public List<string> Roles { get; set; }
public SecurityProfileViewModel SecurityProfile { get; set; }
public string HighestRole {
get
{
string _highestRole = "";
foreach (string role in Roles)
{
switch (role)
{
case "Admin":
// there is no higher
_highestRole = role;
break;
case "Gamer":
// only one high is Admin, so check for that
if (_highestRole != "Admin")
{
_highestRole = role;
}
break;
case "Player":
// make sure _highestRole isn't already set to Gamer or Admin
if (_highestRole != "Admin" && _highestRole != "Gamer")
{
_highestRole = role;
}
break;
default:
_highestRole = "Player";
break;
}
}
return _highestRole;
}
}
}
}

View File

@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class VerifyAuthenticatorCodeViewModel
{
[Required]
public string Code { get; set; }
public string ReturnUrl { get; set; }
[Display(Name = "Remember this browser?")]
public bool RememberBrowser { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}

View File

@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class VerifyCodeViewModel
{
[Required]
public string Provider { get; set; }
[Required]
public string Code { get; set; }
public string ReturnUrl { get; set; }
[Display(Name = "Remember this browser?")]
public bool RememberBrowser { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}

View File

@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
namespace Authentication;
public class VerifyPhoneNumberViewModel
{
[Required]
public string Code { get; set; }
[Required]
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using gaseous_tools;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {

View File

@@ -4,24 +4,31 @@ using System.IO.Compression;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using Authentication;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_server.Controllers; using gaseous_server.Controllers;
using gaseous_server.Models; using gaseous_server.Models;
using gaseous_tools;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Identity;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
public class Collections public class Collections
{ {
public Collections() private readonly UserManager<ApplicationUser> _userManager;
{ private readonly SignInManager<ApplicationUser> _signInManager;
} public Collections(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
public static List<CollectionItem> GetCollections() { public static List<CollectionItem> GetCollections() {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM RomCollections ORDER BY `Name`"; string sql = "SELECT * FROM RomCollections ORDER BY `Name`";
DataTable data = db.ExecuteCMD(sql); DataTable data = db.ExecuteCMD(sql);
@@ -36,7 +43,7 @@ namespace gaseous_server.Classes
} }
public static CollectionItem GetCollection(long Id) { public static CollectionItem GetCollection(long Id) {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM RomCollections WHERE Id = @id ORDER BY `Name`"; string sql = "SELECT * FROM RomCollections WHERE Id = @id ORDER BY `Name`";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", Id); dbDict.Add("id", Id);
@@ -57,7 +64,7 @@ namespace gaseous_server.Classes
public static CollectionItem NewCollection(CollectionItem item) public static CollectionItem NewCollection(CollectionItem item)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, AlwaysInclude, BuiltStatus) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @folderstructure, @includebiosfiles, @alwaysinclude, @builtstatus); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, AlwaysInclude, BuiltStatus) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @folderstructure, @includebiosfiles, @alwaysinclude, @builtstatus); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("name", item.Name); dbDict.Add("name", item.Name);
@@ -88,7 +95,7 @@ namespace gaseous_server.Classes
public static CollectionItem EditCollection(long Id, CollectionItem item, bool ForceRebuild = true) public static CollectionItem EditCollection(long Id, CollectionItem item, bool ForceRebuild = true)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, AlwaysInclude=@alwaysinclude, BuiltStatus=@builtstatus WHERE Id=@id"; string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, AlwaysInclude=@alwaysinclude, BuiltStatus=@builtstatus WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", Id); dbDict.Add("id", Id);
@@ -143,7 +150,7 @@ namespace gaseous_server.Classes
public static void DeleteCollection(long Id) public static void DeleteCollection(long Id)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM RomCollections WHERE Id=@id"; string sql = "DELETE FROM RomCollections WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", Id); dbDict.Add("id", Id);
@@ -163,7 +170,7 @@ namespace gaseous_server.Classes
if (collectionItem.BuildStatus != CollectionItem.CollectionBuildStatus.Building) if (collectionItem.BuildStatus != CollectionItem.CollectionBuildStatus.Building)
{ {
// set collection item to waitingforbuild // set collection item to waitingforbuild
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE RomCollections SET BuiltStatus=@bs WHERE Id=@id"; string sql = "UPDATE RomCollections SET BuiltStatus=@bs WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", Id); dbDict.Add("id", Id);
@@ -212,8 +219,8 @@ namespace gaseous_server.Classes
} }
} else { } else {
// get all platforms to pull from // get all platforms to pull from
FilterController filterController = new FilterController(); Dictionary<string, object> FilterDict = Filters.Filter(AgeRatings.AgeGroups.AgeRestrictionGroupings.Adult, true);
platforms.AddRange((List<Platform>)filterController.Filter()["platforms"]); platforms.AddRange((List<Filters.FilterPlatform>)FilterDict["platforms"]);
} }
// build collection // build collection
@@ -266,7 +273,7 @@ namespace gaseous_server.Classes
gameItem.InclusionStatus.PlatformId = alwaysIncludeItem.PlatformId; gameItem.InclusionStatus.PlatformId = alwaysIncludeItem.PlatformId;
gameItem.InclusionStatus.GameId = alwaysIncludeItem.GameId; gameItem.InclusionStatus.GameId = alwaysIncludeItem.GameId;
gameItem.InclusionStatus.InclusionState = alwaysIncludeItem.InclusionState; gameItem.InclusionStatus.InclusionState = alwaysIncludeItem.InclusionState;
gameItem.Roms = Roms.GetRoms((long)gameItem.Id, (long)platform.Id); gameItem.Roms = Roms.GetRoms((long)gameItem.Id, (long)platform.Id).GameRomItems;
collectionPlatformItem.Games.Add(gameItem); collectionPlatformItem.Games.Add(gameItem);
} }
@@ -286,7 +293,7 @@ namespace gaseous_server.Classes
{ {
CollectionContents.CollectionPlatformItem.CollectionGameItem collectionGameItem = new CollectionContents.CollectionPlatformItem.CollectionGameItem(game); CollectionContents.CollectionPlatformItem.CollectionGameItem collectionGameItem = new CollectionContents.CollectionPlatformItem.CollectionGameItem(game);
List<Roms.GameRomItem> gameRoms = Roms.GetRoms((long)game.Id, (long)platform.Id); List<Roms.GameRomItem> gameRoms = Roms.GetRoms((long)game.Id, (long)platform.Id).GameRomItems;
bool AddGame = false; bool AddGame = false;
@@ -363,7 +370,7 @@ namespace gaseous_server.Classes
public static void CompileCollections(long CollectionId) public static void CompileCollections(long CollectionId)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
CollectionItem collectionItem = GetCollection(CollectionId); CollectionItem collectionItem = GetCollection(CollectionId);
if (collectionItem.BuildStatus == CollectionItem.CollectionBuildStatus.WaitingForBuild) if (collectionItem.BuildStatus == CollectionItem.CollectionBuildStatus.WaitingForBuild)
@@ -416,7 +423,7 @@ namespace gaseous_server.Classes
if (File.Exists(biosItem.biosPath)) if (File.Exists(biosItem.biosPath))
{ {
Logging.Log(Logging.LogType.Information, "Collections", "Copying BIOS file: " + biosItem.filename); Logging.Log(Logging.LogType.Information, "Collections", "Copying BIOS file: " + biosItem.filename);
File.Copy(biosItem.biosPath, Path.Combine(ZipBiosPath, biosItem.filename)); File.Copy(biosItem.biosPath, Path.Combine(ZipBiosPath, biosItem.filename), true);
} }
} }
} }

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Security.Cryptography; using System.Security.Cryptography;
namespace gaseous_tools namespace gaseous_server.Classes
{ {
public class Common public class Common
{ {

View File

@@ -1,10 +1,9 @@
using System; using System;
using System.Data; using System.Data;
using Google.Protobuf.WellKnownTypes;
using Newtonsoft.Json; using Newtonsoft.Json;
using IGDB.Models; using IGDB.Models;
namespace gaseous_tools namespace gaseous_server.Classes
{ {
public static class Config public static class Config
{ {
@@ -313,6 +312,16 @@ namespace gaseous_tools
return dbConnString; return dbConnString;
} }
} }
[JsonIgnore]
public string ConnectionStringNoDatabase
{
get
{
string dbConnString = "server=" + HostName + ";port=" + Port + ";userid=" + UserName + ";password=" + Password + ";";
return dbConnString;
}
}
} }
public class Library public class Library
@@ -337,13 +346,29 @@ namespace gaseous_tools
} }
} }
// public string LibraryDataDirectory public string LibraryImportErrorDirectory
// { {
// get get
// { {
// return Path.Combine(LibraryRootDirectory, "Library"); return Path.Combine(LibraryRootDirectory, "Import Errors");
// } }
// } }
public string LibraryImportDuplicatesDirectory
{
get
{
return Path.Combine(LibraryImportErrorDirectory, "Duplicates");
}
}
public string LibraryImportGeneralErrorDirectory
{
get
{
return Path.Combine(LibraryImportErrorDirectory, "Error");
}
}
public string LibraryBIOSDirectory public string LibraryBIOSDirectory
{ {
@@ -385,6 +410,14 @@ namespace gaseous_tools
} }
} }
public string LibraryMediaGroupDirectory
{
get
{
return Path.Combine(LibraryRootDirectory, "Media Groups");
}
}
public string LibraryMetadataDirectory_Platform(Platform platform) public string LibraryMetadataDirectory_Platform(Platform platform)
{ {
string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Platforms", platform.Slug); string MetadataPath = Path.Combine(LibraryMetadataDirectory, "Platforms", platform.Slug);
@@ -470,11 +503,7 @@ namespace gaseous_tools
// log retention in days // log retention in days
public int LogRetention = 7; public int LogRetention = 7;
public enum LoggingFormat public bool AlwaysLogToDisk = false;
{
Json,
Text
}
} }
} }
} }

View File

@@ -3,10 +3,9 @@ using System.Data;
using System.Data.SqlClient; using System.Data.SqlClient;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
using MySql.Data.MySqlClient; using MySqlConnector;
using static gaseous_tools.Database;
namespace gaseous_tools namespace gaseous_server.Classes
{ {
public class Database public class Database
{ {
@@ -69,7 +68,7 @@ namespace gaseous_tools
ExecuteCMD(sql, dbDict, 30, "server=" + Config.DatabaseConfiguration.HostName + ";port=" + Config.DatabaseConfiguration.Port + ";userid=" + Config.DatabaseConfiguration.UserName + ";password=" + Config.DatabaseConfiguration.Password); ExecuteCMD(sql, dbDict, 30, "server=" + Config.DatabaseConfiguration.HostName + ";port=" + Config.DatabaseConfiguration.Port + ";userid=" + Config.DatabaseConfiguration.UserName + ";password=" + Config.DatabaseConfiguration.Password);
// check if schema version table is in place - if not, create the schema version table // check if schema version table is in place - if not, create the schema version table
sql = "SELECT TABLE_SCHEMA, TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'gaseous' AND TABLE_NAME = 'schema_version';"; sql = "SELECT TABLE_SCHEMA, TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = '" + Config.DatabaseConfiguration.DatabaseName + "' AND TABLE_NAME = 'schema_version';";
DataTable SchemaVersionPresent = ExecuteCMD(sql, dbDict); DataTable SchemaVersionPresent = ExecuteCMD(sql, dbDict);
if (SchemaVersionPresent.Rows.Count == 0) if (SchemaVersionPresent.Rows.Count == 0)
{ {
@@ -81,7 +80,7 @@ namespace gaseous_tools
for (int i = 1000; i < 10000; i++) for (int i = 1000; i < 10000; i++)
{ {
string resourceName = "gaseous_tools.Database.MySQL.gaseous-" + i + ".sql"; string resourceName = "gaseous_server.Support.Database.MySQL.gaseous-" + i + ".sql";
string dbScript = ""; string dbScript = "";
string[] resources = Assembly.GetExecutingAssembly().GetManifestResourceNames(); string[] resources = Assembly.GetExecutingAssembly().GetManifestResourceNames();
@@ -109,7 +108,7 @@ namespace gaseous_tools
if (SchemaVer < i) if (SchemaVer < i)
{ {
// run pre-upgrade code // run pre-upgrade code
gaseous_tools.DatabaseMigration.PreUpgradeScript(i, _ConnectorType); DatabaseMigration.PreUpgradeScript(i, _ConnectorType);
// apply schema! // apply schema!
Logging.Log(Logging.LogType.Information, "Database", "Updating schema to version " + i); Logging.Log(Logging.LogType.Information, "Database", "Updating schema to version " + i);
@@ -121,7 +120,7 @@ namespace gaseous_tools
ExecuteCMD(sql, dbDict); ExecuteCMD(sql, dbDict);
// run post-upgrade code // run post-upgrade code
gaseous_tools.DatabaseMigration.PostUpgradeScript(i, _ConnectorType); DatabaseMigration.PostUpgradeScript(i, _ConnectorType);
} }
} }
} }
@@ -148,6 +147,50 @@ namespace gaseous_tools
return _ExecuteCMD(Command, Parameters, Timeout, ConnectionString); return _ExecuteCMD(Command, Parameters, Timeout, ConnectionString);
} }
public List<Dictionary<string, object>> ExecuteCMDDict(string Command)
{
Dictionary<string, object> dbDict = new Dictionary<string, object>();
return _ExecuteCMDDict(Command, dbDict, 30, "");
}
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters)
{
return _ExecuteCMDDict(Command, Parameters, 30, "");
}
public List<Dictionary<string, object>> ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
{
return _ExecuteCMDDict(Command, Parameters, Timeout, ConnectionString);
}
private List<Dictionary<string, object>> _ExecuteCMDDict(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
{
DataTable dataTable = _ExecuteCMD(Command, Parameters, Timeout, ConnectionString);
// convert datatable to dictionary
List<Dictionary<string, object?>> rows = new List<Dictionary<string, object?>>();
foreach (DataRow dataRow in dataTable.Rows)
{
Dictionary<string, object?> row = new Dictionary<string, object?>();
for (int i = 0; i < dataRow.Table.Columns.Count; i++)
{
string columnName = dataRow.Table.Columns[i].ColumnName;
if (dataRow[i] == System.DBNull.Value)
{
row.Add(columnName, null);
}
else
{
row.Add(columnName, dataRow[i].ToString());
}
}
rows.Add(row);
}
return rows;
}
private DataTable _ExecuteCMD(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "") private DataTable _ExecuteCMD(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
{ {
if (ConnectionString == "") { ConnectionString = _ConnectionString; } if (ConnectionString == "") { ConnectionString = _ConnectionString; }
@@ -161,6 +204,35 @@ namespace gaseous_tools
} }
} }
public int ExecuteNonQuery(string Command)
{
Dictionary<string, object> dbDict = new Dictionary<string, object>();
return _ExecuteNonQuery(Command, dbDict, 30, "");
}
public int ExecuteNonQuery(string Command, Dictionary<string, object> Parameters)
{
return _ExecuteNonQuery(Command, Parameters, 30, "");
}
public int ExecuteNonQuery(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
{
return _ExecuteNonQuery(Command, Parameters, Timeout, ConnectionString);
}
private int _ExecuteNonQuery(string Command, Dictionary<string, object> Parameters, int Timeout = 30, string ConnectionString = "")
{
if (ConnectionString == "") { ConnectionString = _ConnectionString; }
switch (_ConnectorType)
{
case databaseType.MySql:
MySQLServerConnector conn = new MySQLServerConnector(ConnectionString);
return (int)conn.ExecNonQuery(Command, Parameters, Timeout);
default:
return 0;
}
}
public void ExecuteTransactionCMD(List<SQLTransactionItem> CommandList, int Timeout = 60) public void ExecuteTransactionCMD(List<SQLTransactionItem> CommandList, int Timeout = 60)
{ {
object conn; object conn;
@@ -285,6 +357,47 @@ namespace gaseous_tools
return RetTable; return RetTable;
} }
public int ExecNonQuery(string SQL, Dictionary< string, object> Parameters, int Timeout)
{
int result = 0;
Logging.Log(Logging.LogType.Debug, "Database", "Connecting to database", null, true);
MySqlConnection conn = new MySqlConnection(DBConn);
conn.Open();
MySqlCommand cmd = new MySqlCommand
{
Connection = conn,
CommandText = SQL,
CommandTimeout = Timeout
};
foreach (string Parameter in Parameters.Keys)
{
cmd.Parameters.AddWithValue(Parameter, Parameters[Parameter]);
}
try
{
Logging.Log(Logging.LogType.Debug, "Database", "Executing sql: '" + SQL + "'", null, true);
if (Parameters.Count > 0)
{
string dictValues = string.Join(";", Parameters.Select(x => string.Join("=", x.Key, x.Value)));
Logging.Log(Logging.LogType.Debug, "Database", "Parameters: " + dictValues, null, true);
}
result = cmd.ExecuteNonQuery();
} catch (Exception ex) {
Logging.Log(Logging.LogType.Critical, "Database", "Error while executing '" + SQL + "'", ex);
Trace.WriteLine("Error executing " + SQL);
Trace.WriteLine("Full exception: " + ex.ToString());
}
Logging.Log(Logging.LogType.Debug, "Database", "Closing database connection", null, true);
conn.Close();
return result;
}
public void TransactionExecCMD(List<Dictionary<string, object>> Parameters, int Timeout) public void TransactionExecCMD(List<Dictionary<string, object>> Parameters, int Timeout)
{ {
var conn = new MySqlConnection(DBConn); var conn = new MySqlConnection(DBConn);

View File

@@ -1,20 +1,20 @@
using System; using System;
using System.Data; using System.Data;
namespace gaseous_tools namespace gaseous_server.Classes
{ {
public static class DatabaseMigration public static class DatabaseMigration
{ {
public static List<int> BackgroundUpgradeTargetSchemaVersions = new List<int>(); public static List<int> BackgroundUpgradeTargetSchemaVersions = new List<int>();
public static void PreUpgradeScript(int TargetSchemaVersion, gaseous_tools.Database.databaseType? DatabaseType) public static void PreUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
{ {
} }
public static void PostUpgradeScript(int TargetSchemaVersion, gaseous_tools.Database.databaseType? DatabaseType) public static void PostUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
switch(DatabaseType) switch(DatabaseType)
@@ -65,7 +65,7 @@ namespace gaseous_tools
} }
public static void MySql_1002_MigrateMetadataVersion() { public static void MySql_1002_MigrateMetadataVersion() {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();

View File

@@ -0,0 +1,225 @@
using System.Data;
using System.Reflection.Metadata.Ecma335;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
namespace gaseous_server.Classes
{
public class Filters
{
public static Dictionary<string, object> Filter(Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction, bool IncludeUnrated)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Dictionary<string, object> FilterSet = new Dictionary<string, object>();
// platforms
List<FilterPlatform> platforms = new List<FilterPlatform>();
string ageRestriction_Platform = "Game.AgeGroupId <= " + (int)MaximumAgeRestriction;
string ageRestriction_Generic = "view_Games.AgeGroupId <= " + (int)MaximumAgeRestriction;
if (IncludeUnrated == true)
{
ageRestriction_Platform += " OR Game.AgeGroupId IS NULL";
ageRestriction_Generic += " OR view_Games.AgeGroupId IS NULL";
}
string sql = "SELECT DISTINCT Platform.Id, Platform.Abbreviation, Platform.AlternativeName, Platform.`Name`, Platform.PlatformLogo, (SELECT COUNT(*) AS GameCount FROM (SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Games_Roms.PlatformId, view_Games.AgeGroupId FROM Games_Roms LEFT JOIN view_Games ON view_Games.Id = Games_Roms.GameId) Game WHERE Game.PlatformId = Platform.Id AND (" + ageRestriction_Platform + ")) AS GameCount FROM Platform LEFT JOIN Relation_Game_Platforms ON Relation_Game_Platforms.PlatformsId = Platform.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_Platforms.GameId HAVING GameCount > 0 ORDER BY Platform.`Name`;";
DataTable dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
FilterPlatform platformItem = new FilterPlatform(Classes.Metadata.Platforms.GetPlatform((long)dr["id"]));
platformItem.GameCount = (int)(long)dr["GameCount"];
platforms.Add(platformItem);
}
FilterSet.Add("platforms", platforms);
// genres
List<FilterGenre> genres = new List<FilterGenre>();
dbResponse = GetGenericFilterItem(db, "Genre", ageRestriction_Generic);
foreach (DataRow dr in dbResponse.Rows)
{
FilterGenre genreItem = new FilterGenre(Classes.Metadata.Genres.GetGenres((long)dr["id"]));
genreItem.GameCount = (int)(long)dr["GameCount"];
genres.Add(genreItem);
}
FilterSet.Add("genres", genres);
// game modes
List<FilterGameMode> gameModes = new List<FilterGameMode>();
dbResponse = GetGenericFilterItem(db, "GameMode", ageRestriction_Generic);
foreach (DataRow dr in dbResponse.Rows)
{
FilterGameMode gameModeItem = new FilterGameMode(Classes.Metadata.GameModes.GetGame_Modes((long)dr["id"]));
gameModeItem.GameCount = (int)(long)dr["GameCount"];
gameModes.Add(gameModeItem);
}
FilterSet.Add("gamemodes", gameModes);
// player perspectives
List<FilterPlayerPerspective> playerPerspectives = new List<FilterPlayerPerspective>();
dbResponse = GetGenericFilterItem(db, "PlayerPerspective", ageRestriction_Generic);
foreach (DataRow dr in dbResponse.Rows)
{
FilterPlayerPerspective playerPerspectiveItem = new FilterPlayerPerspective(Classes.Metadata.PlayerPerspectives.GetGame_PlayerPerspectives((long)dr["id"]));
playerPerspectiveItem.GameCount = (int)(long)dr["GameCount"];
playerPerspectives.Add(playerPerspectiveItem);
}
FilterSet.Add("playerperspectives", playerPerspectives);
// themes
List<FilterTheme> themes = new List<FilterTheme>();
dbResponse = GetGenericFilterItem(db, "Theme", ageRestriction_Generic);
foreach (DataRow dr in dbResponse.Rows)
{
FilterTheme themeItem = new FilterTheme(Classes.Metadata.Themes.GetGame_Themes((long)dr["id"]));
themeItem.GameCount = (int)(long)dr["GameCount"];
themes.Add(themeItem);
}
FilterSet.Add("themes", themes);
// age groups
List<FilterAgeGrouping> agegroupings = new List<FilterAgeGrouping>();
sql = "SELECT view_Games.Id, view_Games.AgeGroupId, COUNT(view_Games.Id) AS GameCount FROM view_Games WHERE (" + ageRestriction_Generic + ") GROUP BY view_Games.AgeGroupId ORDER BY view_Games.AgeGroupId DESC;";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
FilterAgeGrouping filterAgeGrouping = new FilterAgeGrouping();
if (dr["AgeGroupId"] == DBNull.Value)
{
filterAgeGrouping.Id = (long)AgeRatings.AgeGroups.AgeRestrictionGroupings.Unclassified;
filterAgeGrouping.AgeGroup = AgeRatings.AgeGroups.AgeRestrictionGroupings.Unclassified;
}
else
{
filterAgeGrouping.Id = (long)(AgeRatings.AgeGroups.AgeRestrictionGroupings)dr["AgeGroupId"];
filterAgeGrouping.AgeGroup = (AgeRatings.AgeGroups.AgeRestrictionGroupings)dr["AgeGroupId"];
}
filterAgeGrouping.GameCount = (int)(long)dr["GameCount"];
agegroupings.Add(filterAgeGrouping);
}
FilterSet.Add("agegroupings", agegroupings);
return FilterSet;
}
private static DataTable GetGenericFilterItem(Database db, string Name, string AgeRestriction_Generic)
{
string sql = "SELECT DISTINCT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(view_Games.Id) AS GameCount FROM <ITEMNAME> LEFT JOIN Relation_Game_<ITEMNAME>s ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_<ITEMNAME>s.GameId WHERE (" + AgeRestriction_Generic + ") GROUP BY <ITEMNAME>.Id ORDER BY <ITEMNAME>.`Name`;";
sql = sql.Replace("<ITEMNAME>", Name);
DataTable dbResponse = db.ExecuteCMD(sql);
return dbResponse;
}
public class FilterPlatform : IGDB.Models.Platform
{
public FilterPlatform(Platform obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterGenre : IGDB.Models.Genre
{
public FilterGenre(Genre obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterGameMode : IGDB.Models.GameMode
{
public FilterGameMode(GameMode obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterPlayerPerspective : IGDB.Models.PlayerPerspective
{
public FilterPlayerPerspective(PlayerPerspective obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterTheme : IGDB.Models.Theme
{
public FilterTheme(Theme obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterAgeGrouping
{
public long Id { get; set; }
public AgeRatings.AgeGroups.AgeRestrictionGroupings AgeGroup { get ; set; }
public string Name
{
get
{
return this.AgeGroup.ToString();
}
}
public int GameCount { get; set; }
}
}
}

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Data; using System.Data;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_tools;
using IGDB.Models; using IGDB.Models;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
@@ -39,12 +39,17 @@ namespace gaseous_server
{ {
get get
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM GameLibraries WHERE DefaultLibrary=1 LIMIT 1"; string sql = "SELECT * FROM GameLibraries WHERE DefaultLibrary=1 LIMIT 1";
DataTable data = db.ExecuteCMD(sql); DataTable data = db.ExecuteCMD(sql);
DataRow row = data.Rows[0]; DataRow row = data.Rows[0];
LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"])); LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"]));
if (!Directory.Exists(library.Path))
{
Directory.CreateDirectory(library.Path);
}
return library; return library;
} }
} }
@@ -54,13 +59,22 @@ namespace gaseous_server
get get
{ {
List<LibraryItem> libraryItems = new List<LibraryItem>(); List<LibraryItem> libraryItems = new List<LibraryItem>();
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM GameLibraries"; string sql = "SELECT * FROM GameLibraries";
DataTable data = db.ExecuteCMD(sql); DataTable data = db.ExecuteCMD(sql);
foreach (DataRow row in data.Rows) foreach (DataRow row in data.Rows)
{ {
LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"])); LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"]));
libraryItems.Add(library); libraryItems.Add(library);
if (library.IsDefaultLibrary == true)
{
// check directory exists
if (!Directory.Exists(library.Path))
{
Directory.CreateDirectory(library.Path);
}
}
} }
return libraryItems; return libraryItems;
@@ -86,7 +100,7 @@ namespace gaseous_server
throw new PathNotFound(PathName); throw new PathNotFound(PathName);
} }
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "INSERT INTO GameLibraries (Name, Path, DefaultPlatform, DefaultLibrary) VALUES (@name, @path, @defaultplatform, 0); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; string sql = "INSERT INTO GameLibraries (Name, Path, DefaultPlatform, DefaultLibrary) VALUES (@name, @path, @defaultplatform, 0); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("name", Name); dbDict.Add("name", Name);
@@ -103,7 +117,7 @@ namespace gaseous_server
{ {
if (GetLibrary(LibraryId).IsDefaultLibrary == false) if (GetLibrary(LibraryId).IsDefaultLibrary == false)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM Games_Roms WHERE LibraryId=@id; DELETE FROM GameLibraries WHERE Id=@id;"; string sql = "DELETE FROM Games_Roms WHERE LibraryId=@id; DELETE FROM GameLibraries WHERE Id=@id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", LibraryId); dbDict.Add("id", LibraryId);
@@ -117,7 +131,7 @@ namespace gaseous_server
public static LibraryItem GetLibrary(int LibraryId) public static LibraryItem GetLibrary(int LibraryId)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM GameLibraries WHERE Id=@id"; string sql = "SELECT * FROM GameLibraries WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", LibraryId); dbDict.Add("id", LibraryId);
@@ -143,6 +157,11 @@ namespace gaseous_server
_Path = Path; _Path = Path;
_DefaultPlatformId = DefaultPlatformId; _DefaultPlatformId = DefaultPlatformId;
_IsDefaultLibrary = IsDefaultLibrary; _IsDefaultLibrary = IsDefaultLibrary;
if (!Directory.Exists(Path))
{
Directory.CreateDirectory(Path);
}
} }
int _Id = 0; int _Id = 0;

View File

@@ -5,10 +5,8 @@ using System.Security.Policy;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_tools;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI; using NuGet.Common;
using Org.BouncyCastle.Utilities.IO.Pem;
using static gaseous_server.Classes.Metadata.Games; using static gaseous_server.Classes.Metadata.Games;
namespace gaseous_server.Classes namespace gaseous_server.Classes
@@ -24,8 +22,13 @@ namespace gaseous_server.Classes
// import files first // import files first
foreach (string importContent in importContents_Files) { foreach (string importContent in importContents_Files) {
ImportGame.ImportGameFile(importContent, null, false); ImportGame.ImportGameFile(importContent, null);
} }
// import sub directories
foreach (string importDir in importContents_Directories) {
Classes.ImportGames importGames = new Classes.ImportGames(importDir);
}
} }
else else
{ {
@@ -39,9 +42,9 @@ namespace gaseous_server.Classes
public class ImportGame public class ImportGame
{ {
public static void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform, bool IsDirectory = false) public static void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
@@ -51,76 +54,89 @@ namespace gaseous_server.Classes
} }
else else
{ {
if (IsDirectory == false) FileInfo fi = new FileInfo(GameFileImportPath);
{ Common.hashObject hash = new Common.hashObject(GameFileImportPath);
FileInfo fi = new FileInfo(GameFileImportPath);
Common.hashObject hash = new Common.hashObject(GameFileImportPath);
Models.PlatformMapping.PlatformMapItem? IsBios = Classes.Bios.BiosHashSignatureLookup(hash.md5hash); Models.PlatformMapping.PlatformMapItem? IsBios = Classes.Bios.BiosHashSignatureLookup(hash.md5hash);
if (IsBios == null) if (IsBios == null)
{
// file is a rom
// check to make sure we don't already have this file imported
sql = "SELECT COUNT(Id) AS count FROM Games_Roms WHERE MD5=@md5 AND SHA1=@sha1";
dbDict.Add("md5", hash.md5hash);
dbDict.Add("sha1", hash.sha1hash);
DataTable importDB = db.ExecuteCMD(sql, dbDict);
if ((Int64)importDB.Rows[0]["count"] > 0)
{ {
// file is a rom // import source was the import directory
// check to make sure we don't already have this file imported if (GameFileImportPath.StartsWith(Config.LibraryConfiguration.LibraryImportDirectory))
sql = "SELECT COUNT(Id) AS count FROM Games_Roms WHERE MD5=@md5 AND SHA1=@sha1";
dbDict.Add("md5", hash.md5hash);
dbDict.Add("sha1", hash.sha1hash);
DataTable importDB = db.ExecuteCMD(sql, dbDict);
if ((Int64)importDB.Rows[0]["count"] > 0)
{ {
if (!GameFileImportPath.StartsWith(Config.LibraryConfiguration.LibraryImportDirectory)) Logging.Log(Logging.LogType.Warning, "Import Game", " " + GameFileImportPath + " already in database - moving to " + Config.LibraryConfiguration.LibraryImportDuplicatesDirectory);
string targetPathWithFileName = GameFileImportPath.Replace(Config.LibraryConfiguration.LibraryImportDirectory, Config.LibraryConfiguration.LibraryImportDuplicatesDirectory);
string targetPath = Path.GetDirectoryName(targetPathWithFileName);
if (!Directory.Exists(targetPath))
{ {
Logging.Log(Logging.LogType.Warning, "Import Game", " " + GameFileImportPath + " already in database - skipping"); Directory.CreateDirectory(targetPath);
} }
File.Move(GameFileImportPath, targetPathWithFileName, true);
} }
else
// import source was the upload directory
if (GameFileImportPath.StartsWith(Config.LibraryConfiguration.LibraryUploadDirectory))
{ {
Logging.Log(Logging.LogType.Information, "Import Game", " " + GameFileImportPath + " not in database - processing"); Logging.Log(Logging.LogType.Warning, "Import Game", " " + GameFileImportPath + " already in database - skipping import");
Models.Signatures_Games discoveredSignature = GetFileSignature(hash, fi, GameFileImportPath);
// get discovered platform
IGDB.Models.Platform? determinedPlatform = null;
if (OverridePlatform == null)
{
determinedPlatform = Metadata.Platforms.GetPlatform(discoveredSignature.Flags.IGDBPlatformId);
if (determinedPlatform == null)
{
determinedPlatform = new IGDB.Models.Platform();
}
}
else
{
determinedPlatform = OverridePlatform;
discoveredSignature.Flags.IGDBPlatformId = (long)determinedPlatform.Id;
discoveredSignature.Flags.IGDBPlatformName = determinedPlatform.Name;
}
IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature.Game.Name, discoveredSignature.Flags.IGDBPlatformId);
// add to database
StoreROM(GameLibrary.GetDefaultLibrary, hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath);
} }
} }
else else
{ {
// file is a bios Logging.Log(Logging.LogType.Information, "Import Game", " " + GameFileImportPath + " not in database - processing");
if (IsBios.WebEmulator != null)
Models.Signatures_Games discoveredSignature = GetFileSignature(hash, fi, GameFileImportPath);
// get discovered platform
IGDB.Models.Platform? determinedPlatform = null;
if (OverridePlatform == null)
{ {
foreach (Classes.Bios.BiosItem biosItem in Classes.Bios.GetBios()) determinedPlatform = Metadata.Platforms.GetPlatform(discoveredSignature.Flags.IGDBPlatformId);
if (determinedPlatform == null)
{ {
if (biosItem.Available == false && biosItem.hash == hash.md5hash) determinedPlatform = new IGDB.Models.Platform();
}
}
else
{
determinedPlatform = OverridePlatform;
discoveredSignature.Flags.IGDBPlatformId = (long)determinedPlatform.Id;
discoveredSignature.Flags.IGDBPlatformName = determinedPlatform.Name;
}
IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature.Game.Name, discoveredSignature.Flags.IGDBPlatformId);
// add to database
StoreROM(GameLibrary.GetDefaultLibrary, hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath);
}
}
else
{
// file is a bios
if (IsBios.WebEmulator != null)
{
foreach (Classes.Bios.BiosItem biosItem in Classes.Bios.GetBios())
{
if (biosItem.Available == false && biosItem.hash == hash.md5hash)
{
string biosPath = biosItem.biosPath.Replace(biosItem.filename, "");
if (!Directory.Exists(biosPath))
{ {
string biosPath = biosItem.biosPath.Replace(biosItem.filename, ""); Directory.CreateDirectory(biosPath);
if (!Directory.Exists(biosPath))
{
Directory.CreateDirectory(biosPath);
}
File.Move(GameFileImportPath, biosItem.biosPath, true);
break;
} }
File.Move(GameFileImportPath, biosItem.biosPath, true);
break;
} }
} }
} }
@@ -138,35 +154,42 @@ namespace gaseous_server.Classes
// extract the zip file and search the contents // extract the zip file and search the contents
string ExtractPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, Path.GetRandomFileName()); string ExtractPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, Path.GetRandomFileName());
if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); } if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); }
ZipFile.ExtractToDirectory(GameFileImportPath, ExtractPath); try
// loop through contents until we find the first signature match
foreach (string file in Directory.GetFiles(ExtractPath))
{ {
FileInfo zfi = new FileInfo(file); ZipFile.ExtractToDirectory(GameFileImportPath, ExtractPath);
Common.hashObject zhash = new Common.hashObject(file);
Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi, file); // loop through contents until we find the first signature match
zDiscoveredSignature.Rom.Name = Path.ChangeExtension(zDiscoveredSignature.Rom.Name, ".zip"); foreach (string file in Directory.GetFiles(ExtractPath))
if (zDiscoveredSignature.Score > discoveredSignature.Score)
{ {
if ( FileInfo zfi = new FileInfo(file);
zDiscoveredSignature.Rom.SignatureSource == gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.MAMEArcade || Common.hashObject zhash = new Common.hashObject(file);
zDiscoveredSignature.Rom.SignatureSource == gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.MAMEMess
)
{
zDiscoveredSignature.Rom.Name = zDiscoveredSignature.Game.Description + ".zip";
}
zDiscoveredSignature.Rom.Crc = discoveredSignature.Rom.Crc;
zDiscoveredSignature.Rom.Md5 = discoveredSignature.Rom.Md5;
zDiscoveredSignature.Rom.Sha1 = discoveredSignature.Rom.Sha1;
zDiscoveredSignature.Rom.Size = discoveredSignature.Rom.Size;
discoveredSignature = zDiscoveredSignature;
break; Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi, file);
zDiscoveredSignature.Rom.Name = Path.ChangeExtension(zDiscoveredSignature.Rom.Name, ".zip");
if (zDiscoveredSignature.Score > discoveredSignature.Score)
{
if (
zDiscoveredSignature.Rom.SignatureSource == gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.MAMEArcade ||
zDiscoveredSignature.Rom.SignatureSource == gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.MAMEMess
)
{
zDiscoveredSignature.Rom.Name = zDiscoveredSignature.Game.Description + ".zip";
}
zDiscoveredSignature.Rom.Crc = discoveredSignature.Rom.Crc;
zDiscoveredSignature.Rom.Md5 = discoveredSignature.Rom.Md5;
zDiscoveredSignature.Rom.Sha1 = discoveredSignature.Rom.Sha1;
zDiscoveredSignature.Rom.Size = discoveredSignature.Rom.Size;
discoveredSignature = zDiscoveredSignature;
break;
}
} }
} }
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Get Signature", "Error processing zip file: " + GameFileImportPath, ex);
}
if (Directory.Exists(ExtractPath)) { Directory.Delete(ExtractPath, true); } if (Directory.Exists(ExtractPath)) { Directory.Delete(ExtractPath, true); }
} }
@@ -394,7 +417,7 @@ namespace gaseous_server.Classes
public static long StoreROM(GameLibrary.LibraryItem library, Common.hashObject hash, IGDB.Models.Game determinedGame, IGDB.Models.Platform determinedPlatform, Models.Signatures_Games discoveredSignature, string GameFileImportPath, long UpdateId = 0) public static long StoreROM(GameLibrary.LibraryItem library, Common.hashObject hash, IGDB.Models.Game determinedGame, IGDB.Models.Platform determinedPlatform, Models.Signatures_Games discoveredSignature, string GameFileImportPath, long UpdateId = 0)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
@@ -405,7 +428,7 @@ namespace gaseous_server.Classes
sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, Path, MetadataSource, MetadataGameName, MetadataVersion, LibraryId) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, Path, MetadataSource, MetadataGameName, MetadataVersion, LibraryId) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
} else } else
{ {
sql = "UPDATE Games_Roms SET PlatformId=platformid, GameId=@gameid, Name=@name, Size=@size, DevelopmentStatus=@developmentstatus, Attributes=@Attributes, RomType=@romtype, RomTypeMedia=@romtypemedia, MediaLabel=@medialabel, MetadataSource=@metadatasource, MetadataGameName=@metadatagamename, MetadataVersion=@metadataversion WHERE Id=@id;"; sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid, Name=@name, Size=@size, DevelopmentStatus=@developmentstatus, Attributes=@Attributes, RomType=@romtype, RomTypeMedia=@romtypemedia, MediaLabel=@medialabel, MetadataSource=@metadatasource, MetadataGameName=@metadatagamename, MetadataVersion=@metadataversion WHERE Id=@id;";
dbDict.Add("id", UpdateId); dbDict.Add("id", UpdateId);
} }
dbDict.Add("platformid", Common.ReturnValueIfNull(determinedPlatform.Id, 0)); dbDict.Add("platformid", Common.ReturnValueIfNull(determinedPlatform.Id, 0));
@@ -517,7 +540,7 @@ namespace gaseous_server.Classes
File.Move(romPath, DestinationPath); File.Move(romPath, DestinationPath);
// update the db // update the db
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE Games_Roms SET Path=@path WHERE Id=@id"; string sql = "UPDATE Games_Roms SET Path=@path WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId); dbDict.Add("id", RomId);
@@ -542,7 +565,7 @@ namespace gaseous_server.Classes
GameLibrary.LibraryItem library = GameLibrary.GetDefaultLibrary; GameLibrary.LibraryItem library = GameLibrary.GetDefaultLibrary;
// move rom files to their new location // move rom files to their new location
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Games_Roms WHERE LibraryId = @libraryid"; string sql = "SELECT * FROM Games_Roms WHERE LibraryId = @libraryid";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("libraryid", library.Id); dbDict.Add("libraryid", library.Id);
@@ -564,7 +587,7 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Organise Library", "Finsihed default library organisation"); Logging.Log(Logging.LogType.Information, "Organise Library", "Finsihed default library organisation");
} }
private static void DeleteOrphanedDirectories(string startLocation) public static void DeleteOrphanedDirectories(string startLocation)
{ {
foreach (var directory in Directory.GetDirectories(startLocation)) foreach (var directory in Directory.GetDirectories(startLocation))
{ {
@@ -697,37 +720,6 @@ namespace gaseous_server.Classes
if (File.Exists(romPath)) if (File.Exists(romPath))
{ {
// file exists, so lets check to make sure the signature was matched, and update if a signature can be found
if (
romMetadataSource == gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.None ||
(int)dtRoms.Rows[i]["MetadataVersion"] == 1
)
{
Common.hashObject hash = new Common.hashObject
{
md5hash = (string)dtRoms.Rows[i]["MD5"],
sha1hash = (string)dtRoms.Rows[i]["SHA1"]
};
FileInfo fi = new FileInfo(romPath);
Models.Signatures_Games sig = GetFileSignature(hash, fi, romPath);
if (sig.Rom.SignatureSource != gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType.None)
{
Logging.Log(Logging.LogType.Information, "Library Scan", " Update signature found for " + romPath);
// get discovered platform
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId);
if (determinedPlatform == null)
{
determinedPlatform = new IGDB.Models.Platform();
}
IGDB.Models.Game determinedGame = SearchForGame(sig.Game.Name, sig.Flags.IGDBPlatformId);
StoreROM(library, hash, determinedGame, determinedPlatform, sig, romPath, romId);
}
}
if (library.IsDefaultLibrary == true) if (library.IsDefaultLibrary == true)
{ {
if (romPath != ComputeROMPath(romId)) if (romPath != ComputeROMPath(romId))
@@ -753,6 +745,65 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan completed"); Logging.Log(Logging.LogType.Information, "Library Scan", "Library scan completed");
} }
} }
public static void Rematcher(bool ForceExecute = false)
{
// rescan all titles with an unknown platform or title and see if we can get a match
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan starting");
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
if (ForceExecute == false)
{
sql = "SELECT * FROM Games_Roms WHERE ((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) LIMIT 100;";
}
else
{
sql = "SELECT * FROM Games_Roms WHERE ((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0);";
}
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7));
DataTable data = db.ExecuteCMD(sql, dbDict);
foreach (DataRow row in data.Rows)
{
// get library
GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)row["LibraryId"]);
// get rom info
long romId = (long)row["Id"];
string romPath = (string)row["Path"];
Common.hashObject hash = new Common.hashObject
{
md5hash = (string)row["MD5"],
sha1hash = (string)row["SHA1"]
};
FileInfo fi = new FileInfo(romPath);
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Running rematch against " + romPath);
// determine rom signature
Models.Signatures_Games sig = GetFileSignature(hash, fi, romPath);
// determine rom platform
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId);
if (determinedPlatform == null)
{
determinedPlatform = new IGDB.Models.Platform();
}
IGDB.Models.Game determinedGame = SearchForGame(sig.Game.Name, sig.Flags.IGDBPlatformId);
StoreROM(library, hash, determinedGame, determinedPlatform, sig, romPath, romId);
string attemptSql = "UPDATE Games_Roms SET LastMatchAttemptDate=@lastmatchattemptdate WHERE Id=@id;";
Dictionary<string, object> dbLastAttemptDict = new Dictionary<string, object>();
dbLastAttemptDict.Add("id", romId);
dbLastAttemptDict.Add("lastmatchattemptdate", DateTime.UtcNow);
db.ExecuteCMD(attemptSql, dbLastAttemptDict);
}
Logging.Log(Logging.LogType.Information, "Rematch Scan", "Rematch scan completed");
}
} }
} }

View File

@@ -3,11 +3,11 @@ using System.Data;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
using Org.BouncyCastle.Utilities; namespace gaseous_server.Classes
namespace gaseous_tools
{ {
public class Logging public class Logging
{ {
private static DateTime lastDiskRetentionSweep = DateTime.UtcNow;
public static bool WriteToDiskOnly { get; set; } = false; public static bool WriteToDiskOnly { get; set; } = false;
static public void Log(LogType EventType, string ServerProcess, string Message, Exception? ExceptionValue = null, bool LogToDiskOnly = false) static public void Log(LogType EventType, string ServerProcess, string Message, Exception? ExceptionValue = null, bool LogToDiskOnly = false)
@@ -70,7 +70,12 @@ namespace gaseous_tools
if (LogToDiskOnly == false) if (LogToDiskOnly == false)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); if (Config.LoggingConfiguration.AlwaysLogToDisk == true)
{
LogToDisk(logItem, TraceOutput, null);
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate; INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception) VALUES (@EventTime, @EventType, @Process, @Message, @Exception);"; string sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate; INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception) VALUES (@EventTime, @EventType, @Process, @Message, @Exception);";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("EventRententionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1)); dbDict.Add("EventRententionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1));
@@ -94,6 +99,22 @@ namespace gaseous_tools
LogToDisk(logItem, TraceOutput, null); LogToDisk(logItem, TraceOutput, null);
} }
} }
if (lastDiskRetentionSweep.AddMinutes(60) < DateTime.UtcNow)
{
// time to delete any old logs
lastDiskRetentionSweep = DateTime.UtcNow;
string[] files = Directory.GetFiles(Config.LogPath);
foreach (string file in files)
{
FileInfo fi = new FileInfo(file);
if (fi.LastAccessTime < DateTime.Now.AddDays(Config.LoggingConfiguration.LogRetention * -1))
{
fi.Delete();
}
}
}
} }
static void LogToDisk(LogItem logItem, string TraceOutput, Exception? exception) static void LogToDisk(LogItem logItem, string TraceOutput, Exception? exception)
@@ -111,22 +132,82 @@ namespace gaseous_tools
File.AppendAllText(Config.LogFilePath, TraceOutput); File.AppendAllText(Config.LogFilePath, TraceOutput);
} }
static public List<LogItem> GetLogs(long? StartIndex, int PageNumber = 1, int PageSize = 100) static public List<LogItem> GetLogs(LogsViewModel model)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("StartIndex", model.StartIndex);
dbDict.Add("PageNumber", (model.PageNumber - 1) * model.PageSize);
dbDict.Add("PageSize", model.PageSize);
string sql = ""; string sql = "";
if (StartIndex == null)
List<string> whereClauses = new List<string>();
// handle status criteria
if (model.Status != null)
{ {
sql = "SELECT * FROM ServerLogs ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;"; if (model.Status.Count > 0)
{
List<string> statusWhere = new List<string>();
for (int i = 0; i < model.Status.Count; i++)
{
string valueName = "@eventtype" + i;
statusWhere.Add(valueName);
dbDict.Add(valueName, (int)model.Status[i]);
}
whereClauses.Add("EventType IN (" + string.Join(",", statusWhere) + ")");
}
}
// handle start date criteria
if (model.StartDateTime != null)
{
dbDict.Add("startdate", model.StartDateTime);
whereClauses.Add("EventTime >= @startdate");
}
// handle end date criteria
if (model.EndDateTime != null)
{
dbDict.Add("enddate", model.EndDateTime);
whereClauses.Add("EventTime <= @enddate");
}
// handle search text criteria
if (model.SearchText != null)
{
if (model.SearchText.Length > 0)
{
dbDict.Add("messageSearch", model.SearchText);
whereClauses.Add("MATCH(Message) AGAINST (@messageSearch)");
}
}
// compile WHERE clause
string whereClause = "";
if (whereClauses.Count > 0)
{
whereClause = "(" + String.Join(" AND ", whereClauses) + ")";
}
// execute query
if (model.StartIndex == null)
{
if (whereClause.Length > 0)
{
whereClause = "WHERE " + whereClause;
}
sql = "SELECT * FROM ServerLogs " + whereClause + " ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;";
} }
else else
{ {
sql = "SELECT * FROM ServerLogs WHERE Id < @StartIndex ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;"; if (whereClause.Length > 0)
{
whereClause = "AND " + whereClause;
}
sql = "SELECT * FROM ServerLogs WHERE Id < @StartIndex " + whereClause + " ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;";
} }
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("StartIndex", StartIndex);
dbDict.Add("PageNumber", (PageNumber - 1) * PageSize);
dbDict.Add("PageSize", PageSize);
DataTable dataTable = db.ExecuteCMD(sql, dbDict); DataTable dataTable = db.ExecuteCMD(sql, dbDict);
List<LogItem> logs = new List<LogItem>(); List<LogItem> logs = new List<LogItem>();
@@ -176,6 +257,17 @@ namespace gaseous_tools
} }
public string? ExceptionValue { get; set; } public string? ExceptionValue { get; set; }
} }
public class LogsViewModel
{
public long? StartIndex { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 100;
public List<LogType> Status { get; set; } = new List<LogType>();
public DateTime? StartDateTime { get; set; }
public DateTime? EndDateTime { get; set; }
public string? SearchText { get; set; }
}
} }
} }

View File

@@ -0,0 +1,68 @@
using System;
using System.Data;
using gaseous_server.Models;
using Microsoft.VisualStudio.Web.CodeGeneration;
namespace gaseous_server.Classes
{
public class Maintenance
{
const int MaxFileAge = 30;
public static void RunMaintenance()
{
// delete files and directories older than 7 days in PathsToClean
List<string> PathsToClean = new List<string>();
PathsToClean.Add(Config.LibraryConfiguration.LibraryUploadDirectory);
PathsToClean.Add(Config.LibraryConfiguration.LibraryTempDirectory);
foreach (string PathToClean in PathsToClean)
{
Logging.Log(Logging.LogType.Information, "Maintenance", "Removing files older than " + MaxFileAge + " days from " + PathToClean);
// get content
// files first
foreach (string filePath in Directory.GetFiles(PathToClean))
{
FileInfo fileInfo = new FileInfo(filePath);
if (fileInfo.LastWriteTimeUtc.AddDays(MaxFileAge) < DateTime.UtcNow)
{
Logging.Log(Logging.LogType.Warning, "Maintenance", "Deleting file " + filePath);
File.Delete(filePath);
}
}
// now directories
foreach (string dirPath in Directory.GetDirectories(PathToClean))
{
DirectoryInfo directoryInfo = new DirectoryInfo(dirPath);
if (directoryInfo.LastWriteTimeUtc.AddDays(MaxFileAge) < DateTime.UtcNow)
{
Logging.Log(Logging.LogType.Warning, "Maintenance", "Deleting directory " + directoryInfo);
Directory.Delete(dirPath, true);
}
}
}
Logging.Log(Logging.LogType.Information, "Maintenance", "Optimising database tables");
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SHOW TABLES;";
DataTable tables = db.ExecuteCMD(sql);
foreach (DataRow row in tables.Rows)
{
sql = "OPTIMIZE TABLE " + row[0].ToString();
DataTable response = db.ExecuteCMD(sql);
foreach (DataRow responseRow in response.Rows)
{
string retVal = "";
for (int i = 0; i < responseRow.ItemArray.Length; i++)
{
retVal += responseRow.ItemArray[i] + "; ";
}
Logging.Log(Logging.LogType.Information, "Maintenance", "Optimise table " + row[0].ToString() + ": " + retVal);
}
}
}
}
}

View File

@@ -1,10 +1,9 @@
using System; using System;
using System.Reflection; using System.Reflection;
using gaseous_tools; using System.Text.Json.Serialization;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common; using Microsoft.CodeAnalysis.Classification;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -84,7 +83,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<AgeRating>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<AgeRating>(returnValue, "id", (long)searchValue);
} }
break; break;
@@ -154,6 +153,196 @@ namespace gaseous_server.Classes.Metadata
public AgeRatingTitle RatingTitle { get; set; } public AgeRatingTitle RatingTitle { get; set; }
public string[] Descriptions { get; set; } public string[] Descriptions { get; set; }
} }
public static void PopulateAgeMap()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM ClassificationMap;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
db.ExecuteNonQuery(sql);
// loop all age groups
foreach(KeyValuePair<AgeGroups.AgeRestrictionGroupings, AgeGroups.AgeGroupItem> ageGrouping in AgeGroups.AgeGroupingsFlat)
{
AgeGroups.AgeGroupItem ageGroupItem = ageGrouping.Value;
var properties = ageGroupItem.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
List<string> AgeRatingCategories = new List<string>(Enum.GetNames(typeof(AgeRatingCategory)));
if (AgeRatingCategories.Contains(prop.Name))
{
AgeRatingCategory ageRatingCategory = (AgeRatingCategory)Enum.Parse(typeof(AgeRatingCategory), prop.Name);
List<AgeRatingTitle> ageRatingTitles = (List<AgeRatingTitle>)prop.GetValue(ageGroupItem);
foreach (AgeRatingTitle ageRatingTitle in ageRatingTitles)
{
dbDict.Clear();
dbDict.Add("AgeGroupId", ageGrouping.Key);
dbDict.Add("ClassificationBoardId", ageRatingCategory);
dbDict.Add("RatingId", ageRatingTitle);
sql = "INSERT INTO ClassificationMap (AgeGroupId, ClassificationBoardId, RatingId) VALUES (@AgeGroupId, @ClassificationBoardId, @RatingId);";
db.ExecuteCMD(sql, dbDict);
}
}
}
}
}
}
public class AgeGroups
{
public AgeGroups()
{
}
public static Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>> AgeGroupings
{
get
{
return new Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>>{
{
AgeRestrictionGroupings.Adult, new List<AgeGroupItem>{ Adult_Item, Mature_Item, Teen_Item, Child_Item }
},
{
AgeRestrictionGroupings.Mature, new List<AgeGroupItem>{ Mature_Item, Teen_Item, Child_Item }
},
{
AgeRestrictionGroupings.Teen, new List<AgeGroupItem>{ Teen_Item, Child_Item }
},
{
AgeRestrictionGroupings.Child, new List<AgeGroupItem>{ Child_Item }
}
};
}
}
public static Dictionary<AgeRestrictionGroupings, AgeGroupItem> AgeGroupingsFlat
{
get
{
return new Dictionary<AgeRestrictionGroupings, AgeGroupItem>{
{
AgeRestrictionGroupings.Adult, Adult_Item
},
{
AgeRestrictionGroupings.Mature, Mature_Item
},
{
AgeRestrictionGroupings.Teen, Teen_Item
},
{
AgeRestrictionGroupings.Child, Child_Item
}
};
}
}
public enum AgeRestrictionGroupings
{
Adult = 4,
Mature = 3,
Teen = 2,
Child = 1,
Unclassified = 0
}
readonly static AgeGroupItem Adult_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_R18, AgeRatingTitle.ACB_RC },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_Z },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Eighteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.RP, AgeRatingTitle.AO },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Eighteen },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Eighteen},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_18}
};
readonly static AgeGroupItem Mature_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_M, AgeRatingTitle.ACB_MA15 },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_C, AgeRatingTitle.CERO_D },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Sixteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.M },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Fifteen },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Sixteen},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_16}
};
readonly static AgeGroupItem Teen_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_PG },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_B },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_Twelve, AgeRatingTitle.CLASS_IND_Fourteen },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.T },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_Twelve },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Twelve},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_12}
};
readonly static AgeGroupItem Child_Item = new AgeGroupItem{
ACB = new List<AgeRatingTitle>{ AgeRatingTitle.ACB_G },
CERO = new List<AgeRatingTitle>{ AgeRatingTitle.CERO_A },
CLASS_IND = new List<AgeRatingTitle>{ AgeRatingTitle.CLASS_IND_L, AgeRatingTitle.CLASS_IND_Ten },
ESRB = new List<AgeRatingTitle>{ AgeRatingTitle.E, AgeRatingTitle.E10 },
GRAC = new List<AgeRatingTitle>{ AgeRatingTitle.GRAC_All },
PEGI = new List<AgeRatingTitle>{ AgeRatingTitle.Three, AgeRatingTitle.Seven},
USK = new List<AgeRatingTitle>{ AgeRatingTitle.USK_0, AgeRatingTitle.USK_6}
};
public class AgeGroupItem
{
public List<IGDB.Models.AgeRatingTitle> ACB { get; set; }
public List<IGDB.Models.AgeRatingTitle> CERO { get; set; }
public List<IGDB.Models.AgeRatingTitle> CLASS_IND { get; set; }
public List<IGDB.Models.AgeRatingTitle> ESRB { get; set; }
public List<IGDB.Models.AgeRatingTitle> GRAC { get; set; }
public List<IGDB.Models.AgeRatingTitle> PEGI { get; set; }
public List<IGDB.Models.AgeRatingTitle> USK { get; set; }
[JsonIgnore]
[Newtonsoft.Json.JsonIgnore]
public List<long> AgeGroupItemValues
{
get
{
List<long> values = new List<long>();
{
foreach (AgeRatingTitle ageRatingTitle in ACB)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in CERO)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in CLASS_IND)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in ESRB)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in GRAC)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in PEGI)
{
values.Add((long)ageRatingTitle);
}
foreach (AgeRatingTitle ageRatingTitle in USK)
{
values.Add((long)ageRatingTitle);
}
}
return values;
}
}
}
}
} }
} }

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -82,7 +80,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<AgeRatingContentDescription>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<AgeRatingContentDescription>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -82,7 +80,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<AlternativeName>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<AlternativeName>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -85,7 +83,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Artwork>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -82,7 +80,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Collection>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Collection>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,5 +1,4 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
@@ -81,7 +80,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Company>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Company>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -88,7 +86,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<CompanyLogo>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -85,7 +83,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Cover>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -85,7 +83,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<ExternalGame>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<ExternalGame>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -82,7 +80,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Franchise>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Franchise>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -82,7 +80,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<GameMode>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<GameMode>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -82,7 +80,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<GameVideo>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<GameVideo>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Data; using System.Data;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
@@ -15,6 +14,12 @@ namespace gaseous_server.Classes.Metadata
} }
public class InvalidGameId : Exception
{
public InvalidGameId(long Id) : base("Unable to find Game by id " + Id)
{}
}
private static IGDBClient igdb = new IGDBClient( private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app // Found in Twitch Developer portal for your app
Config.IGDB.ClientId, Config.IGDB.ClientId,
@@ -106,10 +111,11 @@ namespace gaseous_server.Classes.Metadata
{ {
returnValue = await GetObjectFromServer(WhereClause); returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true); Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(returnValue, getAllMetadata, followSubGames);
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue);
} }
return returnValue; return returnValue;
@@ -122,21 +128,71 @@ namespace gaseous_server.Classes.Metadata
private static void UpdateSubClasses(Game Game, bool getAllMetadata, bool followSubGames) private static void UpdateSubClasses(Game Game, bool getAllMetadata, bool followSubGames)
{ {
// required metadata
if (Game.Cover != null) if (Game.Cover != null)
{ {
Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game)); Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game));
} }
if (Game.Genres != null)
{
foreach (long GenreId in Game.Genres.Ids)
{
Genre GameGenre = Genres.GetGenres(GenreId);
}
}
if (Game.GameModes != null)
{
foreach (long gameModeId in Game.GameModes.Ids)
{
GameMode gameMode = GameModes.GetGame_Modes(gameModeId);
}
}
if (Game.MultiplayerModes != null)
{
foreach (long multiplayerModeId in Game.MultiplayerModes.Ids)
{
MultiplayerMode multiplayerMode = MultiplayerModes.GetGame_MultiplayerModes(multiplayerModeId);
}
}
if (Game.PlayerPerspectives != null)
{
foreach (long PerspectiveId in Game.PlayerPerspectives.Ids)
{
PlayerPerspective GamePlayPerspective = PlayerPerspectives.GetGame_PlayerPerspectives(PerspectiveId);
}
}
if (Game.Themes != null)
{
foreach (long ThemeId in Game.Themes.Ids)
{
Theme GameTheme = Themes.GetGame_Themes(ThemeId);
}
}
if (Game.AgeRatings != null)
{
foreach (long AgeRatingId in Game.AgeRatings.Ids)
{
AgeRating GameAgeRating = AgeRatings.GetAgeRatings(AgeRatingId);
}
}
if (Game.ReleaseDates != null)
{
foreach (long ReleaseDateId in Game.ReleaseDates.Ids)
{
ReleaseDate GameReleaseDate = ReleaseDates.GetReleaseDates(ReleaseDateId);
}
}
// optional metadata - usually downloaded as needed
if (getAllMetadata == true) if (getAllMetadata == true)
{ {
if (Game.AgeRatings != null)
{
foreach (long AgeRatingId in Game.AgeRatings.Ids)
{
AgeRating GameAgeRating = AgeRatings.GetAgeRatings(AgeRatingId);
}
}
if (Game.AlternativeNames != null) if (Game.AlternativeNames != null)
{ {
foreach (long AlternativeNameId in Game.AlternativeNames.Ids) foreach (long AlternativeNameId in Game.AlternativeNames.Ids)
@@ -196,14 +252,6 @@ namespace gaseous_server.Classes.Metadata
} }
} }
if (Game.Genres != null)
{
foreach (long GenreId in Game.Genres.Ids)
{
Genre GameGenre = Genres.GetGenres(GenreId);
}
}
if (Game.InvolvedCompanies != null) if (Game.InvolvedCompanies != null)
{ {
foreach (long involvedCompanyId in Game.InvolvedCompanies.Ids) foreach (long involvedCompanyId in Game.InvolvedCompanies.Ids)
@@ -212,22 +260,6 @@ namespace gaseous_server.Classes.Metadata
} }
} }
if (Game.GameModes != null)
{
foreach (long gameModeId in Game.GameModes.Ids)
{
GameMode gameMode = GameModes.GetGame_Modes(gameModeId);
}
}
if (Game.MultiplayerModes != null)
{
foreach (long multiplayerModeId in Game.MultiplayerModes.Ids)
{
MultiplayerMode multiplayerMode = MultiplayerModes.GetGame_MultiplayerModes(multiplayerModeId);
}
}
if (Game.Platforms != null) if (Game.Platforms != null)
{ {
foreach (long PlatformId in Game.Platforms.Ids) foreach (long PlatformId in Game.Platforms.Ids)
@@ -236,14 +268,6 @@ namespace gaseous_server.Classes.Metadata
} }
} }
if (Game.PlayerPerspectives != null)
{
foreach (long PerspectiveId in Game.PlayerPerspectives.Ids)
{
PlayerPerspective GamePlayPerspective = PlayerPerspectives.GetGame_PlayerPerspectives(PerspectiveId);
}
}
if (Game.Screenshots != null) if (Game.Screenshots != null)
{ {
foreach (long ScreenshotId in Game.Screenshots.Ids) foreach (long ScreenshotId in Game.Screenshots.Ids)
@@ -252,14 +276,6 @@ namespace gaseous_server.Classes.Metadata
} }
} }
if (Game.Themes != null)
{
foreach (long ThemeId in Game.Themes.Ids)
{
Theme GameTheme = Themes.GetGame_Themes(ThemeId);
}
}
if (Game.Videos != null) if (Game.Videos != null)
{ {
foreach (long GameVideoId in Game.Videos.Ids) foreach (long GameVideoId in Game.Videos.Ids)
@@ -326,5 +342,40 @@ namespace gaseous_server.Classes.Metadata
search = 2, search = 2,
searchNoPlatform = 3 searchNoPlatform = 3
} }
public class MinimalGameItem
{
public MinimalGameItem(Game gameObject)
{
this.Id = gameObject.Id;
this.Name = gameObject.Name;
this.TotalRating = gameObject.TotalRating;
this.TotalRatingCount = gameObject.TotalRatingCount;
this.Cover = gameObject.Cover;
this.Artworks = gameObject.Artworks;
// compile age ratings
this.AgeRatings = new List<AgeRating>();
if (gameObject.AgeRatings != null)
{
foreach (long ageRatingId in gameObject.AgeRatings.Ids)
{
AgeRating? rating = Classes.Metadata.AgeRatings.GetAgeRatings(ageRatingId);
if (rating != null)
{
this.AgeRatings.Add(rating);
}
}
}
}
public long? Id { get; set; }
public string Name { get; set; }
public double? TotalRating { get; set; }
public int? TotalRatingCount { get; set; }
public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; }
public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; }
public List<IGDB.Models.AgeRating> AgeRatings { get; set; }
}
} }
} }

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -82,7 +80,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Genre>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Genre>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,5 +1,4 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
@@ -81,7 +80,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<InvolvedCompany>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<InvolvedCompany>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -82,7 +80,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<MultiplayerMode>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<MultiplayerMode>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -88,7 +86,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<PlatformLogo>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Data; using System.Data;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
@@ -86,7 +85,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<PlatformVersion>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<PlatformVersion>(returnValue, "id", (long)searchValue);
} }
return returnValue; return returnValue;

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Data; using System.Data;
using System.Net; using System.Net;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -84,7 +82,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<PlayerPerspective>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<PlayerPerspective>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -0,0 +1,113 @@
using System;
using IGDB;
using IGDB.Models;
namespace gaseous_server.Classes.Metadata
{
public class ReleaseDates
{
const string fieldList = "fields category,checksum,created_at,date,game,human,m,platform,region,status,updated_at,y;";
public ReleaseDates()
{
}
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static ReleaseDate? GetReleaseDates(long? Id)
{
if ((Id == 0) || (Id == null))
{
return null;
}
else
{
Task<ReleaseDate> RetVal = _GetReleaseDates(SearchUsing.id, Id);
return RetVal.Result;
}
}
public static ReleaseDate GetReleaseDates(string Slug)
{
Task<ReleaseDate> RetVal = _GetReleaseDates(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<ReleaseDate> _GetReleaseDates(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("ReleaseDate", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("ReleaseDate", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
ReleaseDate returnValue = new ReleaseDate();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<ReleaseDate>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<ReleaseDate>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<ReleaseDate> GetObjectFromServer(string WhereClause)
{
// get ReleaseDates metadata
var results = await igdb.QueryAsync<ReleaseDate>(IGDBClient.Endpoints.ReleaseDates, query: fieldList + " " + WhereClause + ";");
var result = results.First();
return result;
}
}
}

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -86,7 +84,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Screenshot>(returnValue, "id", (long)searchValue);
} }
break; break;

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Data; using System.Data;
using System.Reflection; using System.Reflection;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
@@ -67,7 +66,7 @@ namespace gaseous_server.Classes.Metadata
private static CacheStatus _GetCacheStatus(string Endpoint, string SearchField, object SearchValue) private static CacheStatus _GetCacheStatus(string Endpoint, string SearchField, object SearchValue)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT lastUpdated FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField; string sql = "SELECT lastUpdated FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
@@ -153,6 +152,9 @@ namespace gaseous_server.Classes.Metadata
newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue); newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue);
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(newDict["Ids"]); newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(newDict["Ids"]);
objectDict[key.Key] = newObjectValue; objectDict[key.Key] = newObjectValue;
StoreRelations(ObjectTypeName, key.Key, (long)objectDict["Id"], newObjectValue);
break; break;
case "int32[]": case "int32[]":
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue); newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue);
@@ -175,7 +177,7 @@ namespace gaseous_server.Classes.Metadata
} }
// execute sql // execute sql
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
db.ExecuteCMD(sql, objectDict); db.ExecuteCMD(sql, objectDict);
} }
@@ -198,7 +200,7 @@ namespace gaseous_server.Classes.Metadata
} }
} }
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField; string sql = "SELECT * FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
@@ -216,9 +218,19 @@ namespace gaseous_server.Classes.Metadata
{ {
DataRow dataRow = dt.Rows[0]; DataRow dataRow = dt.Rows[0];
object returnObject = BuildCacheObject<T>(EndpointType, dataRow); object returnObject = BuildCacheObject<T>(EndpointType, dataRow);
ObjectCache.Add(Endpoint + SearchValue, new MemoryCacheObject{ try {
Object = returnObject if (!ObjectCache.ContainsKey(Endpoint + SearchValue))
}); {
ObjectCache.Add(Endpoint + SearchValue, new MemoryCacheObject{
Object = returnObject
});
}
}
catch
{
// unable add item to cache
ObjectCache.Clear();
}
return (T)returnObject; return (T)returnObject;
} }
} }
@@ -419,20 +431,60 @@ namespace gaseous_server.Classes.Metadata
return EndpointType; return EndpointType;
} }
private static void StoreRelations(string PrimaryTable, string SecondaryTable, long ObjectId, string Relations)
{
string TableName = "Relation_" + PrimaryTable + "_" + SecondaryTable;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM information_schema.tables WHERE table_schema = '" + Config.DatabaseConfiguration.DatabaseName + "' AND table_name = '" + TableName + "';";
DataTable data = db.ExecuteCMD(sql);
if (data.Rows.Count == 0)
{
// table doesn't exist, create it
sql = "CREATE TABLE `" + Config.DatabaseConfiguration.DatabaseName + "`.`" + TableName + "` (`" + PrimaryTable + "Id` BIGINT NOT NULL, `" + SecondaryTable + "Id` BIGINT NOT NULL, PRIMARY KEY (`" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`), INDEX `idx_PrimaryColumn` (`" + PrimaryTable + "Id` ASC) VISIBLE);";
db.ExecuteCMD(sql);
}
else
{
// clean existing records for this object
sql = "DELETE FROM " + TableName + " WHERE `" + PrimaryTable + "Id` = @objectid";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("objectid", ObjectId);
db.ExecuteCMD(sql, dbDict);
}
// insert data
long[] RelationValues = Newtonsoft.Json.JsonConvert.DeserializeObject<long[]>(Relations);
foreach (long RelationValue in RelationValues)
{
sql = "INSERT INTO " + TableName + " (`" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`) VALUES (@objectid, @relationvalue);";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("objectid", ObjectId);
dbDict.Add("relationvalue", RelationValue);
db.ExecuteCMD(sql, dbDict);
}
}
private static void CacheClean() private static void CacheClean()
{ {
if (ObjectCache == null) try
{
if (ObjectCache == null)
{
ObjectCache = new Dictionary<string, MemoryCacheObject>();
}
Dictionary<string, MemoryCacheObject> workCache = ObjectCache;
foreach (KeyValuePair<string, MemoryCacheObject> objectCache in workCache)
{
if (objectCache.Value.ExpiryTime < DateTime.UtcNow)
{
ObjectCache.Remove(objectCache.Key);
}
}
}
catch
{ {
ObjectCache = new Dictionary<string, MemoryCacheObject>(); ObjectCache = new Dictionary<string, MemoryCacheObject>();
} }
Dictionary<string, MemoryCacheObject> workCache = ObjectCache;
foreach (KeyValuePair<string, MemoryCacheObject> objectCache in workCache)
{
if (objectCache.Value.ExpiryTime < DateTime.UtcNow)
{
ObjectCache.Remove(objectCache.Key);
}
}
} }
private class MemoryCacheObject private class MemoryCacheObject

View File

@@ -1,9 +1,7 @@
using System; using System;
using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using MySqlX.XDevAPI.Common;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -85,7 +83,7 @@ namespace gaseous_server.Classes.Metadata
} }
catch (Exception ex) catch (Exception ex)
{ {
gaseous_tools.Logging.Log(gaseous_tools.Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
return Storage.GetCacheValue<Theme>(returnValue, "id", (long)searchValue); return Storage.GetCacheValue<Theme>(returnValue, "id", (long)searchValue);
} }
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Data; using System.Data;
using gaseous_server.Models; using gaseous_server.Models;
using gaseous_tools;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
@@ -9,7 +8,7 @@ namespace gaseous_server.Classes
{ {
public static void RefreshMetadata(bool forceRefresh = false) public static void RefreshMetadata(bool forceRefresh = false)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
DataTable dt = new DataTable(); DataTable dt = new DataTable();
@@ -31,7 +30,16 @@ namespace gaseous_server.Classes
} }
// update games // update games
sql = "SELECT Id, `Name` FROM Game;"; if (forceRefresh == true)
{
// when forced, only update games with ROMs for
sql = "SELECT Id, `Name` FROM view_GamesWithRoms;";
}
else
{
// when run normally, update all games (since this will honour cache timeouts)
sql = "SELECT Id, `Name` FROM Game;";
}
dt = db.ExecuteCMD(sql); dt = db.ExecuteCMD(sql);
foreach (DataRow dr in dt.Rows) foreach (DataRow dr in dt.Rows)
@@ -39,7 +47,7 @@ namespace gaseous_server.Classes
try try
{ {
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ")"); Logging.Log(Logging.LogType.Information, "Metadata Refresh", "Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ")");
Metadata.Games.GetGame((long)dr["id"], true, true, forceRefresh); Metadata.Games.GetGame((long)dr["id"], true, false, forceRefresh);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -0,0 +1,440 @@
using System;
using System.Data;
using gaseous_signature_parser.models.RomSignatureObject;
using Microsoft.VisualBasic;
using IGDB.Models;
using gaseous_server.Classes.Metadata;
using System.IO.Compression;
namespace gaseous_server.Classes
{
public class RomMediaGroup
{
public class InvalidMediaGroupId : Exception
{
public InvalidMediaGroupId(long Id) : base("Unable to find media group by id " + Id)
{}
}
public static GameRomMediaGroupItem CreateMediaGroup(long GameId, long PlatformId, List<long> RomIds)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "INSERT INTO RomMediaGroup (Status, PlatformId, GameId) VALUES (@status, @platformid, @gameid); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("status", GameRomMediaGroupItem.GroupBuildStatus.WaitingForBuild);
dbDict.Add("gameid", GameId);
dbDict.Add("platformid", PlatformId);
DataTable mgInsert = db.ExecuteCMD(sql, dbDict);
long mgId = (long)mgInsert.Rows[0][0];
foreach (long RomId in RomIds)
{
try
{
Roms.GameRomItem gameRomItem = Roms.GetRom(RomId);
if (gameRomItem.PlatformId == PlatformId)
{
sql = "INSERT INTO RomMediaGroup_Members (GroupId, RomId) VALUES (@groupid, @romid);";
dbDict.Clear();
dbDict.Add("groupid", mgId);
dbDict.Add("romid", RomId);
db.ExecuteCMD(sql, dbDict);
}
else
{
Logging.Log(Logging.LogType.Warning, "Media Group", "Unable to add ROM id " + RomId + " to group. ROM platform is different from group platform.");
}
}
catch (Roms.InvalidRomId irid)
{
Logging.Log(Logging.LogType.Warning, "Media Group", "Unable to add ROM id " + RomId + " to group. ROM doesn't exist", irid);
}
}
StartMediaGroupBuild(mgId);
return GetMediaGroup(mgId);
}
public static GameRomMediaGroupItem GetMediaGroup(long Id)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM RomMediaGroup WHERE Id=@id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", Id);
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
if (dataTable.Rows.Count == 0)
{
throw new InvalidMediaGroupId(Id);
}
else
{
GameRomMediaGroupItem mediaGroupItem = BuildMediaGroupFromRow(dataTable.Rows[0]);
return mediaGroupItem;
}
}
public static List<GameRomMediaGroupItem> GetMediaGroupsFromGameId(long GameId)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM RomMediaGroup WHERE GameId=@gameid;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("gameid", GameId);
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
List<GameRomMediaGroupItem> mediaGroupItems = new List<GameRomMediaGroupItem>();
foreach (DataRow row in dataTable.Rows)
{
mediaGroupItems.Add(BuildMediaGroupFromRow(row));
}
mediaGroupItems.Sort((x, y) => x.PlatformName.CompareTo(y.PlatformName));
return mediaGroupItems;
}
public static GameRomMediaGroupItem EditMediaGroup(long Id, List<long> RomIds)
{
GameRomMediaGroupItem mg = GetMediaGroup(Id);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
// delete roms from group
sql = "DELETE FROM RomMediaGroup_Members WHERE GroupId=@groupid;";
dbDict.Clear();
dbDict.Add("groupid", Id);
db.ExecuteCMD(sql, dbDict);
// add roms to group
foreach (long RomId in RomIds)
{
try
{
Roms.GameRomItem gameRomItem = Roms.GetRom(RomId);
if (gameRomItem.PlatformId == mg.PlatformId)
{
sql = "INSERT INTO RomMediaGroup_Members (GroupId, RomId) VALUES (@groupid, @romid);";
dbDict.Clear();
dbDict.Add("groupid", Id);
dbDict.Add("romid", RomId);
db.ExecuteCMD(sql, dbDict);
}
else
{
Logging.Log(Logging.LogType.Warning, "Media Group", "Unable to add ROM id " + RomId + " to group. ROM platform is different from group platform.");
}
}
catch (Roms.InvalidRomId irid)
{
Logging.Log(Logging.LogType.Warning, "Media Group", "Unable to add ROM id " + RomId + " to group. ROM doesn't exist", irid);
}
}
// set group to rebuild
sql = "UPDATE RomMediaGroup SET Status=1 WHERE GroupId=@groupid;";
dbDict.Clear();
dbDict.Add("groupid", Id);
db.ExecuteCMD(sql, dbDict);
string MediaGroupZipPath = Path.Combine(Config.LibraryConfiguration.LibraryMediaGroupDirectory, Id + ".zip");
if (File.Exists(MediaGroupZipPath))
{
File.Delete(MediaGroupZipPath);
}
StartMediaGroupBuild(Id);
// return to caller
return GetMediaGroup(Id);
}
public static void DeleteMediaGroup(long Id)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM RomMediaGroup WHERE Id=@id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", Id);
db.ExecuteCMD(sql, dbDict);
string MediaGroupZipPath = Path.Combine(Config.LibraryConfiguration.LibraryMediaGroupDirectory, Id + ".zip");
if (File.Exists(MediaGroupZipPath))
{
File.Delete(MediaGroupZipPath);
}
}
internal static GameRomMediaGroupItem BuildMediaGroupFromRow(DataRow row)
{
GameRomMediaGroupItem mediaGroupItem = new GameRomMediaGroupItem();
mediaGroupItem.Id = (long)row["Id"];
mediaGroupItem.Status = (GameRomMediaGroupItem.GroupBuildStatus)row["Status"];
mediaGroupItem.PlatformId = (long)row["PlatformId"];
mediaGroupItem.GameId = (long)row["GameId"];
mediaGroupItem.RomIds = new List<long>();
// get members
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM RomMediaGroup_Members WHERE GroupId=@id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", mediaGroupItem.Id);
DataTable data = db.ExecuteCMD(sql, dbDict);
foreach (DataRow dataRow in data.Rows)
{
mediaGroupItem.RomIds.Add((long)dataRow["RomId"]);
}
return mediaGroupItem;
}
public static void StartMediaGroupBuild(long Id)
{
GameRomMediaGroupItem mediaGroupItem = GetMediaGroup(Id);
if (mediaGroupItem.Status != GameRomMediaGroupItem.GroupBuildStatus.Building)
{
// set collection item to waitingforbuild
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE RomMediaGroup SET Status=@bs WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", Id);
dbDict.Add("bs", GameRomMediaGroupItem.GroupBuildStatus.WaitingForBuild);
db.ExecuteCMD(sql, dbDict);
// start background task
ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(ProcessQueue.QueueItemType.MediaGroupCompiler, 1, false, true);
queueItem.Options = Id;
queueItem.ForceExecute();
ProcessQueue.QueueItems.Add(queueItem);
}
}
public static void CompileMediaGroup(long Id)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
GameRomMediaGroupItem mediaGroupItem = GetMediaGroup(Id);
if (mediaGroupItem.Status == GameRomMediaGroupItem.GroupBuildStatus.WaitingForBuild)
{
Game GameObject = Games.GetGame(mediaGroupItem.GameId, false, false, false);
Platform PlatformObject = Platforms.GetPlatform(mediaGroupItem.PlatformId, false);
Logging.Log(Logging.LogType.Information, "Media Group", "Beginning build of media group: " + GameObject.Name + " for platform " + PlatformObject.Name);
// set starting
string sql = "UPDATE RomMediaGroup SET Status=@bs WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", mediaGroupItem.Id);
dbDict.Add("bs", GameRomMediaGroupItem.GroupBuildStatus.Building);
db.ExecuteCMD(sql, dbDict);
string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMediaGroupDirectory, mediaGroupItem.Id + ".zip");
string ZipFileTempPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, mediaGroupItem.Id.ToString());
try
{
// clean up if needed
if (File.Exists(ZipFilePath))
{
Logging.Log(Logging.LogType.Warning, "Media Group", "Deleting existing build of media group: " + GameObject.Name + " for platform " + PlatformObject.Name);
File.Delete(ZipFilePath);
}
if (Directory.Exists(ZipFileTempPath))
{
Directory.Delete(ZipFileTempPath, true);
}
// gather media group files
Directory.CreateDirectory(ZipFileTempPath);
List<Roms.GameRomItem> romItems = new List<Roms.GameRomItem>();
List<string> M3UFileContents = new List<string>();
foreach (long RomId in mediaGroupItem.RomIds)
{
Roms.GameRomItem rom = Roms.GetRom(RomId);
if (File.Exists(rom.Path))
{
Logging.Log(Logging.LogType.Information, "Media Group", "Copying ROM: " + rom.Name);
File.Copy(rom.Path, Path.Combine(ZipFileTempPath, Path.GetFileName(rom.Path)));
romItems.Add(rom);
}
}
// build m3u
romItems.Sort((a, b) =>
{
if (a.MediaDetail != null)
{
if (a.MediaDetail.Number != null && a.MediaDetail.Side != null)
{
var firstCompare = a.MediaDetail.Number.ToString().CompareTo(b.MediaDetail.Number.ToString());
return firstCompare != 0 ? firstCompare : a.MediaDetail.Side.CompareTo(b.MediaDetail.Side);
}
else if (a.MediaDetail.Number != null && a.MediaDetail.Side == null)
{
return a.MediaDetail.Number.ToString().CompareTo(b.MediaDetail.Number.ToString());
}
else if (a.MediaDetail.Number == null && a.MediaDetail.Side != null)
{
return a.MediaDetail.Side.ToString().CompareTo(b.MediaDetail.Side.ToString());
}
else
{
return a.Name.CompareTo(b.Name);
}
}
else
{
return a.Name.CompareTo(b.Name);
}
}
);
foreach (Roms.GameRomItem romItem in romItems)
{
string M3UFileContent = "";
M3UFileContent += romItem.Name;
if (romItem.MediaLabel.Length == 0)
{
if (romItem.RomTypeMedia.Length > 0)
{
M3UFileContent += "|" + romItem.RomTypeMedia;
}
}
else
{
M3UFileContent += "|" + romItem.MediaLabel;
}
M3UFileContents.Add(M3UFileContent);
}
File.WriteAllText(Path.Combine(ZipFileTempPath, GameObject.Name + ".m3u"), String.Join(Environment.NewLine, M3UFileContents));
// compress to zip
Logging.Log(Logging.LogType.Information, "Media Group", "Compressing media group");
if (!Directory.Exists(Config.LibraryConfiguration.LibraryMediaGroupDirectory))
{
Directory.CreateDirectory(Config.LibraryConfiguration.LibraryMediaGroupDirectory);
}
ZipFile.CreateFromDirectory(ZipFileTempPath, ZipFilePath, CompressionLevel.SmallestSize, false);
// clean up
if (Directory.Exists(ZipFileTempPath))
{
Logging.Log(Logging.LogType.Information, "Media Group", "Cleaning up");
Directory.Delete(ZipFileTempPath, true);
}
// set completed
dbDict["bs"] = GameRomMediaGroupItem.GroupBuildStatus.Completed;
db.ExecuteCMD(sql, dbDict);
}
catch (Exception ex)
{
// clean up
if (Directory.Exists(ZipFileTempPath))
{
Directory.Delete(ZipFileTempPath, true);
}
if (File.Exists(ZipFilePath))
{
File.Delete(ZipFilePath);
}
// set failed
dbDict["bs"] = GameRomMediaGroupItem.GroupBuildStatus.Failed;
db.ExecuteCMD(sql, dbDict);
Logging.Log(Logging.LogType.Critical, "Media Group", "Media Group building has failed", ex);
}
}
}
public class GameRomMediaGroupItem
{
public long Id { get; set; }
public long GameId { get; set; }
public long PlatformId { get; set; }
public string PlatformName {
get
{
try
{
return Platforms.GetPlatform(PlatformId, false).Name;
}
catch
{
return "Unknown";
}
}
}
public List<long> RomIds { get; set; }
private GroupBuildStatus _Status { get; set; }
public GroupBuildStatus Status {
get
{
if (_Status == GroupBuildStatus.Completed)
{
if (File.Exists(MediaGroupZipPath))
{
return GroupBuildStatus.Completed;
}
else
{
return GroupBuildStatus.NoStatus;
}
}
else
{
return _Status;
}
}
set
{
_Status = value;
}
}
public long? Size {
get
{
if (Status == GroupBuildStatus.Completed)
{
if (File.Exists(MediaGroupZipPath))
{
FileInfo fi = new FileInfo(MediaGroupZipPath);
return fi.Length;
}
else
{
return 0;
}
}
else
{
return 0;
}
}
}
internal string MediaGroupZipPath
{
get
{
return Path.Combine(Config.LibraryConfiguration.LibraryMediaGroupDirectory, Id + ".zip");
}
}
public enum GroupBuildStatus
{
NoStatus = 0,
WaitingForBuild = 1,
Building = 2,
Completed = 3,
Failed = 4
}
}
}
}

View File

@@ -1,14 +1,24 @@
using System; using System;
using System.Data; using System.Data;
using gaseous_tools; using gaseous_signature_parser.models.RomSignatureObject;
using static gaseous_server.Classes.RomMediaGroup;
using gaseous_server.Classes.Metadata;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
public class Roms public class Roms
{ {
public static List<GameRomItem> GetRoms(long GameId, long PlatformId = -1) public class InvalidRomId : Exception
{
public InvalidRomId(long Id) : base("Unable to find ROM by id " + Id)
{}
}
public static GameRomObject GetRoms(long GameId, long PlatformId = -1)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); GameRomObject GameRoms = new GameRomObject();
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", GameId); dbDict.Add("id", GameId);
@@ -23,23 +33,25 @@ namespace gaseous_server.Classes
if (romDT.Rows.Count > 0) if (romDT.Rows.Count > 0)
{ {
List<GameRomItem> romItems = new List<GameRomItem>();
foreach (DataRow romDR in romDT.Rows) foreach (DataRow romDR in romDT.Rows)
{ {
romItems.Add(BuildRom(romDR)); GameRoms.GameRomItems.Add(BuildRom(romDR));
} }
return romItems; // get rom media groups
GameRoms.MediaGroups = Classes.RomMediaGroup.GetMediaGroupsFromGameId(GameId);
return GameRoms;
} }
else else
{ {
throw new Exception("Unknown Game Id"); throw new Games.InvalidGameId(GameId);
} }
} }
public static GameRomItem GetRom(long RomId) public static GameRomItem GetRom(long RomId)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Games_Roms WHERE Id = @id"; string sql = "SELECT * FROM Games_Roms WHERE Id = @id";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId); dbDict.Add("id", RomId);
@@ -53,7 +65,7 @@ namespace gaseous_server.Classes
} }
else else
{ {
throw new Exception("Unknown ROM Id"); throw new InvalidRomId(RomId);
} }
} }
@@ -65,7 +77,7 @@ namespace gaseous_server.Classes
// ensure metadata for gameid is present // ensure metadata for gameid is present
IGDB.Models.Game game = Classes.Metadata.Games.GetGame(GameId, false, false, false); IGDB.Models.Game game = Classes.Metadata.Games.GetGame(GameId, false, false, false);
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid WHERE Id = @id"; string sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid WHERE Id = @id";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId); dbDict.Add("id", RomId);
@@ -88,7 +100,7 @@ namespace gaseous_server.Classes
File.Delete(rom.Path); File.Delete(rom.Path);
} }
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM Games_Roms WHERE Id = @id"; string sql = "DELETE FROM Games_Roms WHERE Id = @id";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId); dbDict.Add("id", RomId);
@@ -135,6 +147,12 @@ namespace gaseous_server.Classes
return romItem; return romItem;
} }
public class GameRomObject
{
public List<GameRomMediaGroupItem> MediaGroups { get; set; } = new List<GameRomMediaGroupItem>();
public List<GameRomItem> GameRomItems { get; set; } = new List<GameRomItem>();
}
public class GameRomItem public class GameRomItem
{ {
public long Id { get; set; } public long Id { get; set; }
@@ -153,12 +171,112 @@ namespace gaseous_server.Classes
public List<KeyValuePair<string, object>>? Attributes { get; set;} public List<KeyValuePair<string, object>>? Attributes { get; set;}
public int RomType { get; set; } public int RomType { get; set; }
public string? RomTypeMedia { get; set; } public string? RomTypeMedia { get; set; }
public MediaType? MediaDetail {
get
{
if (RomTypeMedia != null)
{
return new MediaType(Source, RomTypeMedia);
}
else
{
return null;
}
}
}
public string? MediaLabel { get; set; } public string? MediaLabel { get; set; }
public string? Path { get; set; } public string? Path { get; set; }
public gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType Source { get; set; } public RomSignatureObject.Game.Rom.SignatureSourceType Source { get; set; }
public string? SignatureSourceGameTitle { get; set;} public string? SignatureSourceGameTitle { get; set;}
public GameLibrary.LibraryItem Library { get; set; } public GameLibrary.LibraryItem Library { get; set; }
} }
public class MediaType
{
public MediaType(RomSignatureObject.Game.Rom.SignatureSourceType Source, string MediaTypeString)
{
switch (Source)
{
case RomSignatureObject.Game.Rom.SignatureSourceType.TOSEC:
string[] typeString = MediaTypeString.Split(" ");
string inType = "";
foreach (string typeStringVal in typeString)
{
if (inType == "")
{
switch (typeStringVal.ToLower())
{
case "disk":
Media = RomSignatureObject.Game.Rom.RomTypes.Disk;
inType = typeStringVal;
break;
case "disc":
Media = RomSignatureObject.Game.Rom.RomTypes.Disc;
inType = typeStringVal;
break;
case "file":
Media = RomSignatureObject.Game.Rom.RomTypes.File;
inType = typeStringVal;
break;
case "part":
Media = RomSignatureObject.Game.Rom.RomTypes.Part;
inType = typeStringVal;
break;
case "tape":
Media = RomSignatureObject.Game.Rom.RomTypes.Tape;
inType = typeStringVal;
break;
case "of":
inType = typeStringVal;
break;
case "side":
inType = typeStringVal;
break;
}
}
else {
switch (inType.ToLower())
{
case "disk":
case "disc":
case "file":
case "part":
case "tape":
Number = int.Parse(typeStringVal);
break;
case "of":
Count = int.Parse(typeStringVal);
break;
case "side":
Side = typeStringVal;
break;
}
inType = "";
}
}
break;
default:
break;
}
}
public RomSignatureObject.Game.Rom.RomTypes? Media { get; set; }
public int? Number { get; set; }
public int? Count { get; set; }
public string? Side { get; set; }
}
} }
} }

View File

@@ -1,9 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using MySql.Data.MySqlClient; using gaseous_server.Classes;
using gaseous_signature_parser.models.RomSignatureObject; using gaseous_signature_parser.models.RomSignatureObject;
using gaseous_tools;
using MySqlX.XDevAPI;
using System.Data; using System.Data;
namespace gaseous_server.SignatureIngestors.XML namespace gaseous_server.SignatureIngestors.XML
@@ -13,7 +11,7 @@ namespace gaseous_server.SignatureIngestors.XML
public void Import(string SearchPath, gaseous_signature_parser.parser.SignatureParser XMLType) public void Import(string SearchPath, gaseous_signature_parser.parser.SignatureParser XMLType)
{ {
// connect to database // connect to database
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
// process provided files // process provided files
Logging.Log(Logging.LogType.Information, "Signature Ingestor - XML", "Importing from " + SearchPath); Logging.Log(Logging.LogType.Information, "Signature Ingestor - XML", "Importing from " + SearchPath);
@@ -62,6 +60,11 @@ namespace gaseous_server.SignatureIngestors.XML
{ {
sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5"; sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5";
dbDict = new Dictionary<string, object>(); dbDict = new Dictionary<string, object>();
string sourceUriStr = "";
if (Object.Url != null)
{
sourceUriStr = Object.Url.ToString();
}
dbDict.Add("name", Common.ReturnValueIfNull(Object.Name, "")); dbDict.Add("name", Common.ReturnValueIfNull(Object.Name, ""));
dbDict.Add("description", Common.ReturnValueIfNull(Object.Description, "")); dbDict.Add("description", Common.ReturnValueIfNull(Object.Description, ""));
dbDict.Add("category", Common.ReturnValueIfNull(Object.Category, "")); dbDict.Add("category", Common.ReturnValueIfNull(Object.Category, ""));
@@ -69,7 +72,7 @@ namespace gaseous_server.SignatureIngestors.XML
dbDict.Add("author", Common.ReturnValueIfNull(Object.Author, "")); dbDict.Add("author", Common.ReturnValueIfNull(Object.Author, ""));
dbDict.Add("email", Common.ReturnValueIfNull(Object.Email, "")); dbDict.Add("email", Common.ReturnValueIfNull(Object.Email, ""));
dbDict.Add("homepage", Common.ReturnValueIfNull(Object.Homepage, "")); dbDict.Add("homepage", Common.ReturnValueIfNull(Object.Homepage, ""));
dbDict.Add("uri", Common.ReturnValueIfNull(Object.Url, "")); dbDict.Add("uri", sourceUriStr);
dbDict.Add("sourcetype", Common.ReturnValueIfNull(Object.SourceType, "")); dbDict.Add("sourcetype", Common.ReturnValueIfNull(Object.SourceType, ""));
dbDict.Add("sourcemd5", Object.SourceMd5); dbDict.Add("sourcemd5", Object.SourceMd5);
dbDict.Add("sourcesha1", Object.SourceSHA1); dbDict.Add("sourcesha1", Object.SourceSHA1);

View File

@@ -1,84 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using gaseous_tools;
using IGDB.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
{
[Route("api/v1/[controller]")]
[ApiController]
public class FilterController : ControllerBase
{
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
//[ResponseCache(CacheProfileName = "5Minute")]
public Dictionary<string, object> Filter()
{
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Dictionary<string, object> FilterSet = new Dictionary<string, object>();
// platforms
List<Platform> platforms = new List<Platform>();
string sql = "SELECT Platform.Id, Platform.Abbreviation, Platform.AlternativeName, Platform.`Name`, Platform.PlatformLogo, (SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.PlatformId = Platform.Id) AS RomCount FROM Platform HAVING RomCount > 0 ORDER BY `Name`";
DataTable dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
platforms.Add(Classes.Metadata.Platforms.GetPlatform((long)dr["id"]));
}
FilterSet.Add("platforms", platforms);
// genres
List<Genre> genres = new List<Genre>();
sql = "SELECT DISTINCT t1.Id, t1.`Name` FROM Genre AS t1 JOIN (SELECT * FROM Game WHERE (SELECT COUNT(Id) FROM Games_Roms WHERE GameId = Game.Id) > 0) AS t2 ON JSON_CONTAINS(t2.Genres, CAST(t1.Id AS char), '$') ORDER BY t1.`Name`";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
genres.Add(Classes.Metadata.Genres.GetGenres((long)dr["id"]));
}
FilterSet.Add("genres", genres);
// game modes
List<GameMode> gameModes = new List<GameMode>();
sql = "SELECT DISTINCT t1.Id, t1.`Name` FROM GameMode AS t1 JOIN (SELECT * FROM Game WHERE (SELECT COUNT(Id) FROM Games_Roms WHERE GameId = Game.Id) > 0) AS t2 ON JSON_CONTAINS(t2.GameModes, CAST(t1.Id AS char), '$') ORDER BY t1.Id";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
gameModes.Add(Classes.Metadata.GameModes.GetGame_Modes((long)dr["id"]));
}
FilterSet.Add("gamemodes", gameModes);
// player perspectives
List<PlayerPerspective> playerPerspectives = new List<PlayerPerspective>();
sql = "SELECT DISTINCT t1.Id, t1.`Name` FROM PlayerPerspective AS t1 JOIN (SELECT * FROM Game WHERE (SELECT COUNT(Id) FROM Games_Roms WHERE GameId = Game.Id) > 0) AS t2 ON JSON_CONTAINS(t2.PlayerPerspectives, CAST(t1.Id AS char), '$') ORDER BY t1.`Name`";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
playerPerspectives.Add(Classes.Metadata.PlayerPerspectives.GetGame_PlayerPerspectives((long)dr["id"]));
}
FilterSet.Add("playerperspectives", playerPerspectives);
// themes
List<Theme> themes = new List<Theme>();
sql = "SELECT DISTINCT t1.Id, t1.`Name` FROM Theme AS t1 JOIN (SELECT * FROM Game WHERE (SELECT COUNT(Id) FROM Games_Roms WHERE GameId = Game.Id) > 0) AS t2 ON JSON_CONTAINS(t2.Themes, CAST(t1.Id AS char), '$') ORDER BY t1.`Name`";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
themes.Add(Classes.Metadata.Themes.GetGame_Themes((long)dr["id"]));
}
FilterSet.Add("themes", themes);
return FilterSet;
}
}
}

View File

@@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using gaseous_tools;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
{
[ApiController]
[Route("api/v1/[controller]")]
public class LogsController : Controller
{
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public List<Logging.LogItem> Logs(long? StartIndex, int PageNumber = 1, int PageSize = 100)
{
return Logging.GetLogs(StartIndex, PageNumber, PageSize);
}
}
}

View File

@@ -1,114 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using gaseous_tools;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
{
[ApiController]
[Route("api/v1/[controller]")]
public class SystemController : Controller
{
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public SystemInfo GetSystemStatus()
{
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
SystemInfo ReturnValue = new SystemInfo();
// disk size
List<SystemInfo.PathItem> Disks = new List<SystemInfo.PathItem>();
foreach (GameLibrary.LibraryItem libraryItem in GameLibrary.GetLibraries)
{
Disks.Add(GetDisk(libraryItem.Path));
}
ReturnValue.Paths = Disks;
// database size
string sql = "SELECT table_schema, SUM(data_length + index_length) FROM information_schema.tables WHERE table_schema = '" + Config.DatabaseConfiguration.DatabaseName + "'";
DataTable dbResponse = db.ExecuteCMD(sql);
ReturnValue.DatabaseSize = (long)(System.Decimal)dbResponse.Rows[0][1];
// platform statistics
sql = "SELECT Platform.`name`, grc.Count, grs.Size FROM Platform INNER JOIN (SELECT Platform.`name` AS `Name`, SUM(grs.Size) AS Size FROM Platform JOIN Games_Roms AS grs ON (grs.PlatformId = Platform.Id) GROUP BY Platform.`name`) grs ON (grs.`Name` = Platform.`name`) INNER JOIN (SELECT Platform.`name` AS `Name`, COUNT(grc.Size) AS Count FROM Platform JOIN Games_Roms AS grc ON (grc.PlatformId = Platform.Id) GROUP BY Platform.`name`) grc ON (grc.`Name` = Platform.`name`) ORDER BY Platform.`name`;";
dbResponse = db.ExecuteCMD(sql);
ReturnValue.PlatformStatistics = new List<SystemInfo.PlatformStatisticsItem>();
foreach (DataRow dr in dbResponse.Rows)
{
SystemInfo.PlatformStatisticsItem platformStatisticsItem = new SystemInfo.PlatformStatisticsItem
{
Platform = (string)dr["name"],
RomCount = (long)dr["Count"],
TotalSize = (long)(System.Decimal)dr["Size"]
};
ReturnValue.PlatformStatistics.Add(platformStatisticsItem);
}
return ReturnValue;
}
[HttpGet]
[Route("Version")]
[ProducesResponseType(StatusCodes.Status200OK)]
public Version GetSystemVersion() {
return Assembly.GetExecutingAssembly().GetName().Version;
}
[HttpGet]
[Route("VersionFile")]
[ProducesResponseType(StatusCodes.Status200OK)]
public FileContentResult GetSystemVersionAsFile() {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string ver = "var AppVersion = \"" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\";" + Environment.NewLine +
"var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";";
byte[] bytes = Encoding.UTF8.GetBytes(ver);
return File(bytes, "text/javascript");
}
private SystemInfo.PathItem GetDisk(string Path)
{
SystemInfo.PathItem pathItem = new SystemInfo.PathItem {
LibraryPath = Path,
SpaceUsed = gaseous_tools.Common.DirSize(new DirectoryInfo(Path)),
SpaceAvailable = new DriveInfo(Path).AvailableFreeSpace,
TotalSpace = new DriveInfo(Path).TotalSize
};
return pathItem;
}
public class SystemInfo
{
public Version ApplicationVersion {
get
{
return Assembly.GetExecutingAssembly().GetName().Version;
}
}
public List<PathItem>? Paths { get; set; }
public long DatabaseSize { get; set; }
public List<PlatformStatisticsItem>? PlatformStatistics { get; set; }
public class PathItem
{
public string LibraryPath { get; set; }
public long SpaceUsed { get; set; }
public long SpaceAvailable { get; set; }
public long TotalSpace { get; set; }
}
public class PlatformStatisticsItem
{
public string Platform { get; set; }
public long RomCount { get; set; }
public long TotalSize { get; set; }
}
}
}
}

View File

@@ -0,0 +1,417 @@
using System.Data;
using System.Security.Claims;
using System.Text;
using Authentication;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace gaseous_server.Controllers
{
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ILogger _logger;
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ILoggerFactory loggerFactory)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_logger = loggerFactory.CreateLogger<AccountController>();
}
[HttpPost]
[AllowAnonymous]
[Route("Login")]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
Logging.Log(Logging.LogType.Information, "Login", model.Email + " has logged in, from IP: " + HttpContext.Connection.RemoteIpAddress?.ToString());
return Ok(result.ToString());
}
// if (result.RequiresTwoFactor)
// {
// return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
// }
if (result.IsLockedOut)
{
Logging.Log(Logging.LogType.Warning, "Login", model.Email + " was unable to login due to a locked account. Login attempt from IP: " + HttpContext.Connection.RemoteIpAddress?.ToString());
return Unauthorized();
}
else
{
Logging.Log(Logging.LogType.Critical, "Login", "An unknown error occurred during login by " + model.Email + ". Login attempt from IP: " + HttpContext.Connection.RemoteIpAddress?.ToString());
return Unauthorized();
}
}
// If we got this far, something failed, redisplay form
Logging.Log(Logging.LogType.Critical, "Login", "An unknown error occurred during login by " + model.Email + ". Login attempt from IP: " + HttpContext.Connection.RemoteIpAddress?.ToString());
return Unauthorized();
}
[HttpPost]
[Route("LogOff")]
public async Task<IActionResult> LogOff()
{
var userName = User.FindFirstValue(ClaimTypes.Name);
await _signInManager.SignOutAsync();
if (userName != null)
{
Logging.Log(Logging.LogType.Information, "Login", userName + " has logged out");
}
return Ok();
}
[HttpGet]
[Route("Profile/Basic")]
[Authorize]
public async Task<IActionResult> ProfileBasic()
{
ProfileBasicViewModel profile = new ProfileBasicViewModel();
profile.UserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
ApplicationUser user = await _userManager.FindByIdAsync(profile.UserId);
profile.UserName = _userManager.GetUserName(HttpContext.User);
profile.EmailAddress = await _userManager.GetEmailAsync(user);
profile.Roles = new List<string>(await _userManager.GetRolesAsync(user));
profile.SecurityProfile = user.SecurityProfile;
profile.UserPreferences = user.UserPreferences;
profile.Roles.Sort();
return Ok(profile);
}
[HttpGet]
[Route("Profile/Basic/profile.js")]
[ApiExplorerSettings(IgnoreApi = true)]
[AllowAnonymous]
public async Task<IActionResult> ProfileBasicFile()
{
var user = await _userManager.GetUserAsync(User);
if (user != null)
{
ProfileBasicViewModel profile = new ProfileBasicViewModel();
profile.UserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
profile.UserName = _userManager.GetUserName(HttpContext.User);
profile.EmailAddress = await _userManager.GetEmailAsync(user);
profile.Roles = new List<string>(await _userManager.GetRolesAsync(user));
profile.SecurityProfile = user.SecurityProfile;
profile.UserPreferences = user.UserPreferences;
profile.Roles.Sort();
string profileString = "var userProfile = " + Newtonsoft.Json.JsonConvert.SerializeObject(profile, Newtonsoft.Json.Formatting.Indented) + ";";
byte[] bytes = Encoding.UTF8.GetBytes(profileString);
return File(bytes, "text/javascript");
}
else
{
string profileString = "var userProfile = null;";
byte[] bytes = Encoding.UTF8.GetBytes(profileString);
return File(bytes, "text/javascript");
}
}
[HttpPost]
[Route("ChangePassword")]
[Authorize]
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return RedirectToAction("Login");
}
// ChangePasswordAsync changes the user password
var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
// The new password did not meet the complexity rules or
// the current password is incorrect. Add these errors to
// the ModelState and rerender ChangePassword view
if (!result.Succeeded)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return Unauthorized(result);
}
// Upon successfully changing the password refresh sign-in cookie
await _signInManager.RefreshSignInAsync(user);
return Ok();
}
return NotFound();
}
[HttpGet]
[Route("Users")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> GetAllUsers()
{
List<UserViewModel> users = new List<UserViewModel>();
foreach (ApplicationUser rawUser in _userManager.Users)
{
UserViewModel user = new UserViewModel();
user.Id = rawUser.Id;
user.EmailAddress = rawUser.NormalizedEmail.ToLower();
user.LockoutEnabled = rawUser.LockoutEnabled;
user.LockoutEnd = rawUser.LockoutEnd;
user.SecurityProfile = rawUser.SecurityProfile;
// get roles
ApplicationUser? aUser = await _userManager.FindByIdAsync(rawUser.Id);
if (aUser != null)
{
IList<string> aUserRoles = await _userManager.GetRolesAsync(aUser);
user.Roles = aUserRoles.ToList();
user.Roles.Sort();
}
users.Add(user);
}
return Ok(users);
}
[HttpPost]
[Route("Users")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> NewUser(RegisterViewModel model)
{
if (ModelState.IsValid)
{
ApplicationUser user = new ApplicationUser
{
UserName = model.UserName,
NormalizedUserName = model.UserName.ToUpper(),
Email = model.Email,
NormalizedEmail = model.Email.ToUpper()
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// add new users to the player role
await _userManager.AddToRoleAsync(user, "Player");
Logging.Log(Logging.LogType.Information, "User Management", User.FindFirstValue(ClaimTypes.Name) + " created user " + model.Email + " with password.");
return Ok(result);
}
else
{
return Ok(result);
}
}
else
{
return NotFound();
}
}
[HttpGet]
[Route("Users/{UserId}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> GetUser(string UserId)
{
ApplicationUser? rawUser = await _userManager.FindByIdAsync(UserId);
if (rawUser != null)
{
UserViewModel user = new UserViewModel();
user.Id = rawUser.Id;
user.EmailAddress = rawUser.NormalizedEmail.ToLower();
user.LockoutEnabled = rawUser.LockoutEnabled;
user.LockoutEnd = rawUser.LockoutEnd;
user.SecurityProfile = rawUser.SecurityProfile;
// get roles
IList<string> aUserRoles = await _userManager.GetRolesAsync(rawUser);
user.Roles = aUserRoles.ToList();
user.Roles.Sort();
return Ok(user);
}
else
{
return NotFound();
}
}
[HttpDelete]
[Route("Users/{UserId}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> DeleteUser(string UserId)
{
// get user
ApplicationUser? user = await _userManager.FindByIdAsync(UserId);
if (user == null)
{
return NotFound();
}
else
{
await _userManager.DeleteAsync(user);
Logging.Log(Logging.LogType.Information, "User Management", User.FindFirstValue(ClaimTypes.Name) + " deleted user " + user.Email);
return Ok();
}
}
[HttpPost]
[Route("Users/{UserId}/Roles")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> SetUserRoles(string UserId, string RoleName)
{
ApplicationUser? user = await _userManager.FindByIdAsync(UserId);
if (user != null)
{
// get roles
List<string> userRoles = (await _userManager.GetRolesAsync(user)).ToList();
// delete all roles
foreach (string role in userRoles)
{
if ((new string[] { "Admin", "Gamer", "Player" }).Contains(role) )
{
await _userManager.RemoveFromRoleAsync(user, role);
}
}
// add only requested roles
switch (RoleName)
{
case "Admin":
await _userManager.AddToRoleAsync(user, "Admin");
await _userManager.AddToRoleAsync(user, "Gamer");
await _userManager.AddToRoleAsync(user, "Player");
break;
case "Gamer":
await _userManager.AddToRoleAsync(user, "Gamer");
await _userManager.AddToRoleAsync(user, "Player");
break;
case "Player":
await _userManager.AddToRoleAsync(user, "Player");
break;
default:
await _userManager.AddToRoleAsync(user, RoleName);
break;
}
return Ok();
}
else
{
return NotFound();
}
}
[HttpPost]
[Route("Users/{UserId}/SecurityProfile")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> SetUserSecurityProfile(string UserId, SecurityProfileViewModel securityProfile)
{
if (ModelState.IsValid)
{
ApplicationUser? user = await _userManager.FindByIdAsync(UserId);
if (user != null)
{
user.SecurityProfile = securityProfile;
await _userManager.UpdateAsync(user);
return Ok();
}
else
{
return NotFound();
}
}
else
{
return NotFound();
}
}
[HttpPost]
[Route("Users/{UserId}/Password")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> ResetPassword(string UserId, SetPasswordViewModel model)
{
if (ModelState.IsValid)
{
// we can reset the users password
ApplicationUser? user = await _userManager.FindByIdAsync(UserId);
if (user != null)
{
string resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
IdentityResult passwordChangeResult = await _userManager.ResetPasswordAsync(user, resetToken, model.NewPassword);
if (passwordChangeResult.Succeeded == true)
{
return Ok(passwordChangeResult);
}
else
{
return Ok(passwordChangeResult);
}
}
else
{
return NotFound();
}
}
else
{
return NotFound();
}
}
[HttpPost]
[Route("Preferences")]
public async Task<IActionResult> SetPreferences(List<UserPreferenceViewModel> model)
{
ApplicationUser? user = await _userManager.GetUserAsync(User);
if (user == null)
{
return Unauthorized();
}
else
{
user.UserPreferences = model;
await _userManager.UpdateAsync(user);
return Ok();
}
}
}
}

View File

@@ -2,14 +2,20 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
[ApiController] [ApiController]
[Route("api/v1/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize(Roles = "Admin,Gamer,Player")]
public class BackgroundTasksController : Controller public class BackgroundTasksController : Controller
{ {
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<ProcessQueue.QueueItem> GetQueue() public List<ProcessQueue.QueueItem> GetQueue()
@@ -17,10 +23,13 @@ namespace gaseous_server.Controllers
return ProcessQueue.QueueItems; return ProcessQueue.QueueItems;
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{TaskType}")] [Route("{TaskType}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize(Roles = "Admin")]
public ActionResult<ProcessQueue.QueueItem> ForceRun(ProcessQueue.QueueItemType TaskType, Boolean ForceRun) public ActionResult<ProcessQueue.QueueItem> ForceRun(ProcessQueue.QueueItemType TaskType, Boolean ForceRun)
{ {
foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems) foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems)

View File

@@ -3,14 +3,21 @@ using System.Collections.Generic;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
[ApiController] [ApiController]
[Route("api/v1/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class BiosController : Controller public class BiosController : Controller
{ {
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Classes.Bios.BiosItem> GetBios() public List<Classes.Bios.BiosItem> GetBios()
@@ -18,6 +25,8 @@ namespace gaseous_server.Controllers
return Classes.Bios.GetBios(); return Classes.Bios.GetBios();
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{PlatformId}")] [Route("{PlatformId}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
@@ -26,7 +35,11 @@ namespace gaseous_server.Controllers
return Classes.Bios.GetBios(PlatformId, AvailableOnly); return Classes.Bios.GetBios(PlatformId, AvailableOnly);
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead] [HttpHead]
[Route("zip/{PlatformId}")] [Route("zip/{PlatformId}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -37,7 +50,7 @@ namespace gaseous_server.Controllers
{ {
IGDB.Models.Platform platform = Classes.Metadata.Platforms.GetPlatform(PlatformId); IGDB.Models.Platform platform = Classes.Metadata.Platforms.GetPlatform(PlatformId);
string biosPath = Path.Combine(gaseous_tools.Config.LibraryConfiguration.LibraryBIOSDirectory, platform.Slug); string biosPath = Path.Combine(Config.LibraryConfiguration.LibraryBIOSDirectory, platform.Slug);
string tempFile = Path.GetTempFileName(); string tempFile = Path.GetTempFileName();
@@ -59,7 +72,11 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead] [HttpHead]
[Route("{PlatformId}/{BiosName}")] [Route("{PlatformId}/{BiosName}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]

View File

@@ -4,18 +4,24 @@ using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes; using gaseous_server.Classes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
[ApiController] [ApiController]
[Route("api/v1/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class CollectionsController : Controller public class CollectionsController : Controller
{ {
/// <summary> /// <summary>
/// Gets all ROM collections /// Gets all ROM collections
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Classes.Collections.CollectionItem> GetCollections() public List<Classes.Collections.CollectionItem> GetCollections()
@@ -29,6 +35,8 @@ namespace gaseous_server.Controllers
/// <param name="CollectionId"></param> /// <param name="CollectionId"></param>
/// <param name="Build">Set to true to begin the collection build process</param> /// <param name="Build">Set to true to begin the collection build process</param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{CollectionId}")] [Route("{CollectionId}")]
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
@@ -55,6 +63,8 @@ namespace gaseous_server.Controllers
/// </summary> /// </summary>
/// <param name="CollectionId"></param> /// <param name="CollectionId"></param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{CollectionId}/Roms")] [Route("{CollectionId}/Roms")]
[ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)]
@@ -77,8 +87,11 @@ namespace gaseous_server.Controllers
/// </summary> /// </summary>
/// <param name="Item"></param> /// <param name="Item"></param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[Route("Preview")] [Route("Preview")]
[Authorize(Roles = "Admin,Gamer")]
[ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Classes.Collections.CollectionContents.CollectionPlatformItem>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GetCollectionRomsPreview(Classes.Collections.CollectionItem Item) public ActionResult GetCollectionRomsPreview(Classes.Collections.CollectionItem Item)
@@ -98,6 +111,8 @@ namespace gaseous_server.Controllers
/// </summary> /// </summary>
/// <param name="CollectionId"></param> /// <param name="CollectionId"></param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{CollectionId}/Roms/Zip")] [Route("{CollectionId}/Roms/Zip")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -108,7 +123,7 @@ namespace gaseous_server.Controllers
{ {
Classes.Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId); Classes.Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId);
string ZipFilePath = Path.Combine(gaseous_tools.Config.LibraryConfiguration.LibraryCollectionsDirectory, CollectionId + ".zip"); string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, CollectionId + ".zip");
if (System.IO.File.Exists(ZipFilePath)) if (System.IO.File.Exists(ZipFilePath))
{ {
@@ -131,7 +146,10 @@ namespace gaseous_server.Controllers
/// </summary> /// </summary>
/// <param name="Item"></param> /// <param name="Item"></param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[Authorize(Roles = "Admin,Gamer")]
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult NewCollection(Classes.Collections.CollectionItem Item) public ActionResult NewCollection(Classes.Collections.CollectionItem Item)
@@ -152,8 +170,11 @@ namespace gaseous_server.Controllers
/// <param name="CollectionId"></param> /// <param name="CollectionId"></param>
/// <param name="Item"></param> /// <param name="Item"></param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch] [HttpPatch]
[Route("{CollectionId}")] [Route("{CollectionId}")]
[Authorize(Roles = "Admin,Gamer")]
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult EditCollection(long CollectionId, Classes.Collections.CollectionItem Item) public ActionResult EditCollection(long CollectionId, Classes.Collections.CollectionItem Item)
@@ -174,7 +195,10 @@ namespace gaseous_server.Controllers
/// <param name="CollectionId"></param> /// <param name="CollectionId"></param>
/// <param name="Item"></param> /// <param name="Item"></param>
/// <returns></returns> /// <returns></returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch] [HttpPatch]
[Authorize(Roles = "Admin,Gamer")]
[Route("{CollectionId}/AlwaysInclude")] [Route("{CollectionId}/AlwaysInclude")]
[ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -208,7 +232,10 @@ namespace gaseous_server.Controllers
/// Deletes the specified ROM collection /// Deletes the specified ROM collection
/// </summary> /// </summary>
/// <param name="CollectionId"></param> /// <param name="CollectionId"></param>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpDelete] [HttpDelete]
[Authorize(Roles = "Admin,Gamer")]
[Route("{CollectionId}")] [Route("{CollectionId}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using Authentication;
using gaseous_server.Classes;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
[ApiController]
public class FilterController : ControllerBase
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public FilterController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
//[ResponseCache(CacheProfileName = "5Minute")]
public async Task<IActionResult> FilterAsync()
{
var user = await _userManager.GetUserAsync(User);
return Ok(Filters.Filter(user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction, user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated));
}
}
}

View File

@@ -6,21 +6,25 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_tools;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting;
using Org.BouncyCastle.Asn1.X509;
using static gaseous_server.Classes.Metadata.AgeRatings; using static gaseous_server.Classes.Metadata.AgeRatings;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
[Route("api/v1/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
[ApiController] [ApiController]
public class GamesController : ControllerBase public class GamesController : ControllerBase
{ {
[MapToApiVersion("1.0")]
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
public ActionResult Game( public ActionResult Game(
@@ -35,7 +39,7 @@ namespace gaseous_server.Controllers
bool sortdescending = false) bool sortdescending = false)
{ {
return Ok(GetGames(name, platform, genre, gamemode, playerperspective, theme, minrating, maxrating, sortdescending)); return Ok(GetGames(name, platform, genre, gamemode, playerperspective, theme, minrating, maxrating, "Adult", true, true, sortdescending));
} }
public static List<Game> GetGames( public static List<Game> GetGames(
@@ -47,6 +51,9 @@ namespace gaseous_server.Controllers
string theme = "", string theme = "",
int minrating = -1, int minrating = -1,
int maxrating = -1, int maxrating = -1,
string ratinggroup = "Adult",
bool includenullrating = true,
bool sortbynamethe = false,
bool sortdescending = false) bool sortdescending = false)
{ {
string whereClause = ""; string whereClause = "";
@@ -99,7 +106,7 @@ namespace gaseous_server.Controllers
if (genre.Length > 0) if (genre.Length > 0)
{ {
tempVal = "("; tempVal = "Relation_Game_Genres.GenresId IN (";
string[] genreClauseItems = genre.Split(","); string[] genreClauseItems = genre.Split(",");
for (int i = 0; i < genreClauseItems.Length; i++) for (int i = 0; i < genreClauseItems.Length; i++)
{ {
@@ -108,7 +115,7 @@ namespace gaseous_server.Controllers
tempVal += " AND "; tempVal += " AND ";
} }
string genreLabel = "@Genre" + i; string genreLabel = "@Genre" + i;
tempVal += "JSON_CONTAINS(Game.Genres, " + genreLabel + ", '$')"; tempVal += genreLabel;
whereParams.Add(genreLabel, genreClauseItems[i]); whereParams.Add(genreLabel, genreClauseItems[i]);
} }
tempVal += ")"; tempVal += ")";
@@ -117,7 +124,7 @@ namespace gaseous_server.Controllers
if (gamemode.Length > 0) if (gamemode.Length > 0)
{ {
tempVal = "("; tempVal = "Relation_Game_GameModes.GameModesId IN (";
string[] gameModeClauseItems = gamemode.Split(","); string[] gameModeClauseItems = gamemode.Split(",");
for (int i = 0; i < gameModeClauseItems.Length; i++) for (int i = 0; i < gameModeClauseItems.Length; i++)
{ {
@@ -126,7 +133,7 @@ namespace gaseous_server.Controllers
tempVal += " AND "; tempVal += " AND ";
} }
string gameModeLabel = "@GameMode" + i; string gameModeLabel = "@GameMode" + i;
tempVal += "JSON_CONTAINS(Game.GameModes, " + gameModeLabel + ", '$')"; tempVal += gameModeLabel;
whereParams.Add(gameModeLabel, gameModeClauseItems[i]); whereParams.Add(gameModeLabel, gameModeClauseItems[i]);
} }
tempVal += ")"; tempVal += ")";
@@ -135,7 +142,7 @@ namespace gaseous_server.Controllers
if (playerperspective.Length > 0) if (playerperspective.Length > 0)
{ {
tempVal = "("; tempVal = "Relation_Game_PlayerPerspectives.PlayerPerspectivesId IN (";
string[] playerPerspectiveClauseItems = playerperspective.Split(","); string[] playerPerspectiveClauseItems = playerperspective.Split(",");
for (int i = 0; i < playerPerspectiveClauseItems.Length; i++) for (int i = 0; i < playerPerspectiveClauseItems.Length; i++)
{ {
@@ -144,7 +151,7 @@ namespace gaseous_server.Controllers
tempVal += " AND "; tempVal += " AND ";
} }
string playerPerspectiveLabel = "@PlayerPerspective" + i; string playerPerspectiveLabel = "@PlayerPerspective" + i;
tempVal += "JSON_CONTAINS(Game.PlayerPerspectives, " + playerPerspectiveLabel + ", '$')"; tempVal += playerPerspectiveLabel;
whereParams.Add(playerPerspectiveLabel, playerPerspectiveClauseItems[i]); whereParams.Add(playerPerspectiveLabel, playerPerspectiveClauseItems[i]);
} }
tempVal += ")"; tempVal += ")";
@@ -153,7 +160,7 @@ namespace gaseous_server.Controllers
if (theme.Length > 0) if (theme.Length > 0)
{ {
tempVal = "("; tempVal = "Relation_Game_Themes.ThemesId IN (";
string[] themeClauseItems = theme.Split(","); string[] themeClauseItems = theme.Split(",");
for (int i = 0; i < themeClauseItems.Length; i++) for (int i = 0; i < themeClauseItems.Length; i++)
{ {
@@ -162,13 +169,60 @@ namespace gaseous_server.Controllers
tempVal += " AND "; tempVal += " AND ";
} }
string themeLabel = "@Theme" + i; string themeLabel = "@Theme" + i;
tempVal += "JSON_CONTAINS(Game.Themes, " + themeLabel + ", '$')"; tempVal += themeLabel;
whereParams.Add(themeLabel, themeClauseItems[i]); whereParams.Add(themeLabel, themeClauseItems[i]);
} }
tempVal += ")"; tempVal += ")";
whereClauses.Add(tempVal); whereClauses.Add(tempVal);
} }
if (ratinggroup.Length > 0)
{
List<long> AgeClassificationsList = new List<long>();
foreach (string ratingGroup in ratinggroup.Split(','))
{
AgeGroups.AgeRestrictionGroupings ageRestriction = (AgeGroups.AgeRestrictionGroupings)Enum.Parse(typeof(AgeGroups.AgeRestrictionGroupings), ratingGroup);
if (AgeGroups.AgeGroupings.ContainsKey(ageRestriction))
{
List<AgeGroups.AgeGroupItem> ageGroups = AgeGroups.AgeGroupings[ageRestriction];
foreach (AgeGroups.AgeGroupItem ageGroup in ageGroups)
{
AgeClassificationsList.AddRange(ageGroup.AgeGroupItemValues);
}
}
}
if (AgeClassificationsList.Count > 0)
{
tempVal = "(view_AgeRatings.Rating IN (";
for (int i = 0; i < AgeClassificationsList.Count; i++)
{
if (i > 0)
{
tempVal += ", ";
}
string themeLabel = "@Rating" + i;
tempVal += themeLabel;
whereParams.Add(themeLabel, AgeClassificationsList[i]);
}
tempVal += ")";
tempVal += " OR ";
if (includenullrating == true)
{
tempVal += "view_AgeRatings.Rating IS NULL";
}
else
{
tempVal += "view_AgeRatings.Rating IS NOT NULL";
}
tempVal += ")";
whereClauses.Add(tempVal);
}
}
// build where clause // build where clause
if (whereClauses.Count > 0) if (whereClauses.Count > 0)
{ {
@@ -198,14 +252,21 @@ namespace gaseous_server.Controllers
} }
// order by clause // order by clause
string orderByClause = "ORDER BY `Name` ASC"; string orderByField = "Name";
if (sortbynamethe == true)
{
orderByField = "NameThe";
}
string orderByClause = "ORDER BY `" + orderByField + "` ASC";
if (sortdescending == true) if (sortdescending == true)
{ {
orderByClause = "ORDER BY `Name` DESC"; orderByClause = "ORDER BY `" + orderByField + "` DESC";
} }
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Game.Id, Game.AgeRatings, Game.AggregatedRating, Game.AggregatedRatingCount, Game.AlternativeNames, Game.Artworks, Game.Bundles, Game.Category, Game.Collection, Game.Cover, Game.Dlcs, Game.Expansions, Game.ExternalGames, Game.FirstReleaseDate, Game.`Follows`, Game.Franchise, Game.Franchises, Game.GameEngines, Game.GameModes, Game.Genres, Game.Hypes, Game.InvolvedCompanies, Game.Keywords, Game.MultiplayerModes, Game.`Name`, Game.ParentGame, Game.Platforms, Game.PlayerPerspectives, Game.Rating, Game.RatingCount, Game.ReleaseDates, Game.Screenshots, Game.SimilarGames, Game.Slug, Game.StandaloneExpansions, Game.`Status`, Game.StoryLine, Game.Summary, Game.Tags, Game.Themes, Game.TotalRating, Game.TotalRatingCount, Game.VersionParent, Game.VersionTitle, Game.Videos, Game.Websites FROM gaseous.Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId " + whereClause + " " + havingClause + " " + orderByClause; //string sql = "SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Game.* FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
string sql = "SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Game.*, case when Game.`Name` like 'The %' then CONCAT(trim(substr(Game.`Name` from 4)), ', The') else Game.`Name` end as NameThe FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId LEFT JOIN (SELECT Relation_Game_AgeRatings.GameId, AgeRating.* FROM Relation_Game_AgeRatings JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id) view_AgeRatings ON Game.Id = view_AgeRatings.GameId " + whereClause + " " + havingClause + " " + orderByClause;
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>(); List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
@@ -219,6 +280,8 @@ namespace gaseous_server.Controllers
return RetVal; return RetVal;
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}")] [Route("{GameId}")]
[ProducesResponseType(typeof(Game), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Game), StatusCodes.Status200OK)]
@@ -245,6 +308,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/alternativename")] [Route("{GameId}/alternativename")]
[ProducesResponseType(typeof(List<AlternativeName>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<AlternativeName>), StatusCodes.Status200OK)]
@@ -276,6 +341,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/agerating")] [Route("{GameId}/agerating")]
[ProducesResponseType(typeof(List<AgeRatings.GameAgeRating>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<AgeRatings.GameAgeRating>), StatusCodes.Status200OK)]
@@ -307,6 +374,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/agerating/{RatingId}/image")] [Route("{GameId}/agerating/{RatingId}/image")]
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
@@ -386,6 +455,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/artwork")] [Route("{GameId}/artwork")]
[ProducesResponseType(typeof(List<Artwork>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Artwork>), StatusCodes.Status200OK)]
@@ -415,6 +486,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/artwork/{ArtworkId}")] [Route("{GameId}/artwork/{ArtworkId}")]
[ProducesResponseType(typeof(Artwork), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Artwork), StatusCodes.Status200OK)]
@@ -449,6 +522,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/artwork/{ArtworkId}/image")] [Route("{GameId}/artwork/{ArtworkId}/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -503,6 +578,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/cover")] [Route("{GameId}/cover")]
[ProducesResponseType(typeof(Cover), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Cover), StatusCodes.Status200OK)]
@@ -536,6 +613,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/cover/image")] [Route("{GameId}/cover/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -575,6 +654,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/genre")] [Route("{GameId}/genre")]
[ProducesResponseType(typeof(List<Genre>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Genre>), StatusCodes.Status200OK)]
@@ -611,6 +692,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/companies")] [Route("{GameId}/companies")]
[ProducesResponseType(typeof(List<Dictionary<string, object>>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Dictionary<string, object>>), StatusCodes.Status200OK)]
@@ -654,6 +737,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/companies/{CompanyId}")] [Route("{GameId}/companies/{CompanyId}")]
[ProducesResponseType(typeof(Dictionary<string, object>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Dictionary<string, object>), StatusCodes.Status200OK)]
@@ -695,6 +780,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/companies/{CompanyId}/image")] [Route("{GameId}/companies/{CompanyId}/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -738,9 +825,49 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/releasedates")]
[ProducesResponseType(typeof(List<ReleaseDate>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ResponseCache(CacheProfileName = "7Days")]
public ActionResult GameReleaseDates(long GameId)
{
try
{
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
if (gameObject != null)
{
List<ReleaseDate> rdObjects = new List<ReleaseDate>();
if (gameObject.ReleaseDates != null)
{
foreach (long icId in gameObject.ReleaseDates.Ids)
{
ReleaseDate releaseDate = Classes.Metadata.ReleaseDates.GetReleaseDates(icId);
rdObjects.Add(releaseDate);
}
}
return Ok(rdObjects);
}
else
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/roms")] [Route("{GameId}/roms")]
[ProducesResponseType(typeof(List<Classes.Roms.GameRomItem>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Roms.GameRomObject), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
//[ResponseCache(CacheProfileName = "5Minute")] //[ResponseCache(CacheProfileName = "5Minute")]
public ActionResult GameRom(long GameId) public ActionResult GameRom(long GameId)
@@ -749,9 +876,7 @@ namespace gaseous_server.Controllers
{ {
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false); Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
List<Classes.Roms.GameRomItem> roms = Classes.Roms.GetRoms(GameId); return Ok(Classes.Roms.GetRoms(GameId));
return Ok(roms);
} }
catch catch
{ {
@@ -759,6 +884,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/roms/{RomId}")] [Route("{GameId}/roms/{RomId}")]
[ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)]
@@ -786,7 +913,10 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch] [HttpPatch]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/roms/{RomId}")] [Route("{GameId}/roms/{RomId}")]
[ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -813,7 +943,10 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpDelete] [HttpDelete]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/roms/{RomId}")] [Route("{GameId}/roms/{RomId}")]
[ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Classes.Roms.GameRomItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -840,7 +973,11 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead] [HttpHead]
[Route("{GameId}/roms/{RomId}/file")] [Route("{GameId}/roms/{RomId}/file")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -875,7 +1012,11 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead] [HttpHead]
[Route("{GameId}/roms/{RomId}/{FileName}")] [Route("{GameId}/roms/{RomId}/{FileName}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -910,6 +1051,175 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/romgroup/{RomGroupId}")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
//[ResponseCache(CacheProfileName = "5Minute")]
public ActionResult GameRomGroup(long GameId, long RomGroupId)
{
try
{
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId);
if (rom.GameId == GameId)
{
return Ok(rom);
}
else
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/romgroup")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult NewGameRomGroup(long GameId, long PlatformId, [FromBody] List<long> RomIds)
{
try
{
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
try
{
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.CreateMediaGroup(GameId, PlatformId, RomIds);
return Ok(rom);
}
catch
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/romgroup/{RomId}")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GameRomGroupMembers(long GameId, long RomGroupId, [FromBody] List<long> RomIds)
{
try
{
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId);
if (rom.GameId == GameId)
{
rom = Classes.RomMediaGroup.EditMediaGroup(RomGroupId, RomIds);
return Ok(rom);
}
else
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpDelete]
[Authorize(Roles = "Admin,Gamer")]
[Route("{GameId}/romgroup/{RomGroupId}")]
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GameRomGroupDelete(long GameId, long RomGroupId)
{
try
{
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId);
if (rom.GameId == GameId)
{
Classes.RomMediaGroup.DeleteMediaGroup(RomGroupId);
return Ok(rom);
}
else
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpHead]
[Route("{GameId}/romgroup/{RomGroupId}/file")]
[Route("{GameId}/romgroup/{RomGroupId}/{filename}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GameRomGroupFile(long GameId, long RomGroupId, string filename = "")
{
try
{
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId);
if (rom.GameId != GameId)
{
return NotFound();
}
string romFilePath = Path.Combine(Config.LibraryConfiguration.LibraryMediaGroupDirectory, RomGroupId + ".zip");
if (System.IO.File.Exists(romFilePath))
{
FileStream content = new FileStream(romFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
string returnFileName = "";
if (filename == "")
{
returnFileName = gameObject.Name + ".zip";
}
else
{
returnFileName = filename;
}
FileStreamResult response = File(content, "application/octet-stream", returnFileName);
return response;
}
else
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("search")] [Route("search")]
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
@@ -947,6 +1257,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/screenshots")] [Route("{GameId}/screenshots")]
[ProducesResponseType(typeof(List<Screenshot>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Screenshot>), StatusCodes.Status200OK)]
@@ -976,6 +1288,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/screenshots/{ScreenshotId}")] [Route("{GameId}/screenshots/{ScreenshotId}")]
[ProducesResponseType(typeof(Screenshot), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Screenshot), StatusCodes.Status200OK)]
@@ -1008,6 +1322,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/screenshots/{ScreenshotId}/image")] [Route("{GameId}/screenshots/{ScreenshotId}/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
@@ -1050,6 +1366,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{GameId}/videos")] [Route("{GameId}/videos")]
[ProducesResponseType(typeof(List<GameVideo>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<GameVideo>), StatusCodes.Status200OK)]

View File

@@ -3,14 +3,20 @@ using System.Collections.Generic;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
[ApiController] [ApiController]
[Route("api/v1/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize(Roles = "Admin")]
public class LibraryController : Controller public class LibraryController : Controller
{ {
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(List<GameLibrary.LibraryItem>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<GameLibrary.LibraryItem>), StatusCodes.Status200OK)]
public ActionResult GetLibraries() public ActionResult GetLibraries()
@@ -18,6 +24,8 @@ namespace gaseous_server.Controllers
return Ok(GameLibrary.GetLibraries); return Ok(GameLibrary.GetLibraries);
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet("{LibraryId}")] [HttpGet("{LibraryId}")]
[ProducesResponseType(typeof(GameLibrary.LibraryItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(GameLibrary.LibraryItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -33,6 +41,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[ProducesResponseType(typeof(GameLibrary.LibraryItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(GameLibrary.LibraryItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -53,6 +63,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpDelete("{LibraryId}")] [HttpDelete("{LibraryId}")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using gaseous_server.Classes;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
{
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize(Roles = "Admin")]
public class LogsController : Controller
{
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
public List<Logging.LogItem> Logs(Logging.LogsViewModel model)
{
return Logging.GetLogs(model);
}
}
}

View File

@@ -7,18 +7,24 @@ using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_server.Models; using gaseous_server.Models;
using gaseous_tools; using gaseous_server.Classes;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting;
using Microsoft.AspNetCore.Authorization;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
[Route("api/v1/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[ApiController] [ApiController]
[Authorize]
public class PlatformMapsController : Controller public class PlatformMapsController : Controller
{ {
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(List<PlatformMapping.PlatformMapItem>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<PlatformMapping.PlatformMapItem>), StatusCodes.Status200OK)]
public ActionResult GetPlatformMap(bool ResetToDefault = false) public ActionResult GetPlatformMap(bool ResetToDefault = false)
@@ -31,6 +37,8 @@ namespace gaseous_server.Controllers
return Ok(PlatformMapping.PlatformMap); return Ok(PlatformMapping.PlatformMap);
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{PlatformId}")] [Route("{PlatformId}")]
[ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
@@ -56,10 +64,13 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)]
[RequestSizeLimit(long.MaxValue)] [RequestSizeLimit(long.MaxValue)]
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)] [DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> UploadPlatformMap(List<IFormFile> files) public async Task<IActionResult> UploadPlatformMap(List<IFormFile> files)
{ {
Guid sessionid = Guid.NewGuid(); Guid sessionid = Guid.NewGuid();
@@ -110,7 +121,9 @@ namespace gaseous_server.Controllers
return Ok(new { count = files.Count, size }); return Ok(new { count = files.Count, size });
} }
// [HttpPost] // [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
// [Route("{PlatformId}")] // [Route("{PlatformId}")]
// [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)] // [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
// [ProducesResponseType(StatusCodes.Status404NotFound)] // [ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -137,10 +150,13 @@ namespace gaseous_server.Controllers
// } // }
// } // }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPatch] [HttpPatch]
[Route("{PlatformId}")] [Route("{PlatformId}")]
[ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)] [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize(Roles = "Admin")]
public ActionResult EditPlatformMap(long PlatformId, PlatformMapping.PlatformMapItem Map) public ActionResult EditPlatformMap(long PlatformId, PlatformMapping.PlatformMapItem Map)
{ {
try try

View File

@@ -5,20 +5,26 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_server.Models; using gaseous_server.Models;
using gaseous_tools;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
[Route("api/v1/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
[ApiController] [ApiController]
public class PlatformsController : Controller public class PlatformsController : Controller
{ {
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(typeof(List<Platform>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Platform>), StatusCodes.Status200OK)]
public ActionResult Platform() public ActionResult Platform()
@@ -28,9 +34,9 @@ namespace gaseous_server.Controllers
public static List<Platform> GetPlatforms() public static List<Platform> GetPlatforms()
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM gaseous.Platform WHERE Id IN (SELECT DISTINCT PlatformId FROM Games_Roms) ORDER BY `Name` ASC;"; string sql = "SELECT * FROM Platform WHERE Id IN (SELECT DISTINCT PlatformId FROM Games_Roms) ORDER BY `Name` ASC;";
List<Platform> RetVal = new List<Platform>(); List<Platform> RetVal = new List<Platform>();
@@ -43,6 +49,8 @@ namespace gaseous_server.Controllers
return RetVal; return RetVal;
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{PlatformId}")] [Route("{PlatformId}")]
[ProducesResponseType(typeof(Platform), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Platform), StatusCodes.Status200OK)]
@@ -68,6 +76,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{PlatformId}/platformlogo")] [Route("{PlatformId}/platformlogo")]
[ProducesResponseType(typeof(PlatformLogo), StatusCodes.Status200OK)] [ProducesResponseType(typeof(PlatformLogo), StatusCodes.Status200OK)]
@@ -100,6 +110,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("{PlatformId}/platformlogo/image")] [Route("{PlatformId}/platformlogo/image")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]

View File

@@ -6,22 +6,28 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_tools;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting;
using Org.BouncyCastle.Asn1.X509;
using static gaseous_server.Classes.Metadata.AgeRatings; using static gaseous_server.Classes.Metadata.AgeRatings;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
[Route("api/v1/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
[ApiController] [ApiController]
public class RomsController : ControllerBase public class RomsController : ControllerBase
{ {
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[Authorize(Roles = "Admin,Gamer")]
[ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)]
[RequestSizeLimit(long.MaxValue)] [RequestSizeLimit(long.MaxValue)]
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)] [DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]
@@ -71,7 +77,7 @@ namespace gaseous_server.Controllers
// Process uploaded files // Process uploaded files
foreach (Dictionary<string, object> UploadedFile in UploadedFiles) foreach (Dictionary<string, object> UploadedFile in UploadedFiles)
{ {
Classes.ImportGame.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform, false); Classes.ImportGame.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
} }
if (Directory.Exists(workPath)) if (Directory.Exists(workPath))

View File

@@ -2,17 +2,21 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_tools; using gaseous_server.Classes;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using static gaseous_server.Classes.Metadata.Games; using static gaseous_server.Classes.Metadata.Games;
using static gaseous_tools.Config.ConfigFile;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
[ApiController] [ApiController]
[Route("api/v1/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class SearchController : Controller public class SearchController : Controller
{ {
private static IGDBClient igdb = new IGDBClient( private static IGDBClient igdb = new IGDBClient(
@@ -21,6 +25,8 @@ namespace gaseous_server.Controllers
Config.IGDB.Secret Config.IGDB.Secret
); );
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("Platform")] [Route("Platform")]
[ProducesResponseType(typeof(List<Platform>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Platform>), StatusCodes.Status200OK)]
@@ -42,6 +48,8 @@ namespace gaseous_server.Controllers
return results.ToList(); return results.ToList();
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[Route("Game")] [Route("Game")]
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]

View File

@@ -4,7 +4,9 @@ using System.Data;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Threading.Tasks; using System.Threading.Tasks;
using gaseous_tools; using gaseous_server.Classes;
using gaseous_signature_parser.models.RomSignatureObject;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
// For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 // For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
@@ -12,13 +14,18 @@ using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
[ApiController] [ApiController]
[Route("api/v1/[controller]/[action]")] [Route("api/v{version:apiVersion}/[controller]/[action]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class SignaturesController : ControllerBase public class SignaturesController : ControllerBase
{ {
/// <summary> /// <summary>
/// Get the current signature counts from the database /// Get the current signature counts from the database
/// </summary> /// </summary>
/// <returns>Number of sources, publishers, games, and rom signatures in the database</returns> /// <returns>Number of sources, publishers, games, and rom signatures in the database</returns>
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public Models.Signatures_Status Status() public Models.Signatures_Status Status()
@@ -26,6 +33,8 @@ namespace gaseous_server.Controllers
return new Models.Signatures_Status(); return new Models.Signatures_Status();
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Models.Signatures_Games> GetSignature(string md5 = "", string sha1 = "") public List<Models.Signatures_Games> GetSignature(string md5 = "", string sha1 = "")
@@ -39,6 +48,8 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Models.Signatures_Games> GetByTosecName(string TosecName = "") public List<Models.Signatures_Games> GetByTosecName(string TosecName = "")
@@ -54,7 +65,7 @@ namespace gaseous_server.Controllers
private List<Models.Signatures_Games> _GetSignature(string sqlWhere, string searchString) private List<Models.Signatures_Games> _GetSignature(string sqlWhere, string searchString)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT view_Signatures_Games.*, Signatures_Roms.Id AS romid, Signatures_Roms.Name AS romname, Signatures_Roms.Size, Signatures_Roms.CRC, Signatures_Roms.MD5, Signatures_Roms.SHA1, Signatures_Roms.DevelopmentStatus, Signatures_Roms.Attributes, Signatures_Roms.RomType, Signatures_Roms.RomTypeMedia, Signatures_Roms.MediaLabel, Signatures_Roms.MetadataSource FROM Signatures_Roms INNER JOIN view_Signatures_Games ON Signatures_Roms.GameId = view_Signatures_Games.Id WHERE " + sqlWhere; string sql = "SELECT view_Signatures_Games.*, Signatures_Roms.Id AS romid, Signatures_Roms.Name AS romname, Signatures_Roms.Size, Signatures_Roms.CRC, Signatures_Roms.MD5, Signatures_Roms.SHA1, Signatures_Roms.DevelopmentStatus, Signatures_Roms.Attributes, Signatures_Roms.RomType, Signatures_Roms.RomTypeMedia, Signatures_Roms.MediaLabel, Signatures_Roms.MetadataSource FROM Signatures_Roms INNER JOIN view_Signatures_Games ON Signatures_Roms.GameId = view_Signatures_Games.Id WHERE " + sqlWhere;
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("searchString", searchString); dbDict.Add("searchString", searchString);
@@ -92,7 +103,7 @@ namespace gaseous_server.Controllers
Sha1 = ((string)sigDbRow["SHA1"]).ToLower(), Sha1 = ((string)sigDbRow["SHA1"]).ToLower(),
DevelopmentStatus = (string)sigDbRow["DevelopmentStatus"], DevelopmentStatus = (string)sigDbRow["DevelopmentStatus"],
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(sigDbRow["Attributes"], "[]")), Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(sigDbRow["Attributes"], "[]")),
RomType = (Models.Signatures_Games.RomItem.RomTypes)(int)sigDbRow["RomType"], RomType = (RomSignatureObject.Game.Rom.RomTypes)(int)sigDbRow["RomType"],
RomTypeMedia = (string)sigDbRow["RomTypeMedia"], RomTypeMedia = (string)sigDbRow["RomTypeMedia"],
MediaLabel = (string)sigDbRow["MediaLabel"], MediaLabel = (string)sigDbRow["MediaLabel"],
SignatureSource = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(Int32)sigDbRow["MetadataSource"] SignatureSource = (gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType)(Int32)sigDbRow["MetadataSource"]

View File

@@ -0,0 +1,321 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Razor.Hosting;
namespace gaseous_server.Controllers
{
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Authorize]
public class SystemController : Controller
{
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public SystemInfo GetSystemStatus()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
SystemInfo ReturnValue = new SystemInfo();
// disk size
List<SystemInfo.PathItem> Disks = new List<SystemInfo.PathItem>();
foreach (GameLibrary.LibraryItem libraryItem in GameLibrary.GetLibraries)
{
Disks.Add(GetDisk(libraryItem.Path));
}
ReturnValue.Paths = Disks;
// database size
string sql = "SELECT table_schema, SUM(data_length + index_length) FROM information_schema.tables WHERE table_schema = '" + Config.DatabaseConfiguration.DatabaseName + "'";
DataTable dbResponse = db.ExecuteCMD(sql);
ReturnValue.DatabaseSize = (long)(System.Decimal)dbResponse.Rows[0][1];
// platform statistics
sql = "SELECT Platform.`name`, grc.Count, grs.Size FROM Platform INNER JOIN (SELECT Platform.`name` AS `Name`, SUM(grs.Size) AS Size FROM Platform JOIN Games_Roms AS grs ON (grs.PlatformId = Platform.Id) GROUP BY Platform.`name`) grs ON (grs.`Name` = Platform.`name`) INNER JOIN (SELECT Platform.`name` AS `Name`, COUNT(grc.Size) AS Count FROM Platform JOIN Games_Roms AS grc ON (grc.PlatformId = Platform.Id) GROUP BY Platform.`name`) grc ON (grc.`Name` = Platform.`name`) ORDER BY Platform.`name`;";
dbResponse = db.ExecuteCMD(sql);
ReturnValue.PlatformStatistics = new List<SystemInfo.PlatformStatisticsItem>();
foreach (DataRow dr in dbResponse.Rows)
{
SystemInfo.PlatformStatisticsItem platformStatisticsItem = new SystemInfo.PlatformStatisticsItem
{
Platform = (string)dr["name"],
RomCount = (long)dr["Count"],
TotalSize = (long)(System.Decimal)dr["Size"]
};
ReturnValue.PlatformStatistics.Add(platformStatisticsItem);
}
return ReturnValue;
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Version")]
[ProducesResponseType(StatusCodes.Status200OK)]
public Version GetSystemVersion() {
return Assembly.GetExecutingAssembly().GetName().Version;
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("VersionFile")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
public FileContentResult GetSystemVersionAsFile() {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
// get age ratings dictionary
Dictionary<int, string> ClassificationBoardsStrings = new Dictionary<int, string>();
foreach(IGDB.Models.AgeRatingCategory ageRatingCategory in Enum.GetValues(typeof(IGDB.Models.AgeRatingCategory)) )
{
ClassificationBoardsStrings.Add((int)ageRatingCategory, ageRatingCategory.ToString());
}
Dictionary<int, string> AgeRatingsStrings = new Dictionary<int, string>();
foreach(IGDB.Models.AgeRatingTitle ageRatingTitle in Enum.GetValues(typeof(IGDB.Models.AgeRatingTitle)) )
{
AgeRatingsStrings.Add((int)ageRatingTitle, ageRatingTitle.ToString());
}
string ver = "var AppVersion = \"" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\";" + Environment.NewLine +
"var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";" + Environment.NewLine +
"var FirstRunStatus = " + Config.ReadSetting("FirstRunStatus", "0") + ";" + Environment.NewLine +
"var AgeRatingBoardsStrings = " + JsonSerializer.Serialize(ClassificationBoardsStrings, new JsonSerializerOptions{
WriteIndented = true
}) + ";" + Environment.NewLine +
"var AgeRatingStrings = " + JsonSerializer.Serialize(AgeRatingsStrings, new JsonSerializerOptions{
WriteIndented = true
}) + ";" + Environment.NewLine +
"var AgeRatingGroups = " + JsonSerializer.Serialize(AgeRatings.AgeGroups.AgeGroupingsFlat, new JsonSerializerOptions{
WriteIndented = true
}) + ";";
byte[] bytes = Encoding.UTF8.GetBytes(ver);
return File(bytes, "text/javascript");
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Settings/BackgroundTasks/Intervals")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetBackgroundTasks()
{
Dictionary<string, BackgroundTaskItem> Intervals = new Dictionary<string, BackgroundTaskItem>();
Intervals.Add(ProcessQueue.QueueItemType.SignatureIngestor.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.SignatureIngestor));
Intervals.Add(ProcessQueue.QueueItemType.TitleIngestor.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.TitleIngestor));
Intervals.Add(ProcessQueue.QueueItemType.MetadataRefresh.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.MetadataRefresh));
Intervals.Add(ProcessQueue.QueueItemType.OrganiseLibrary.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.OrganiseLibrary));
Intervals.Add(ProcessQueue.QueueItemType.LibraryScan.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.LibraryScan));
Intervals.Add(ProcessQueue.QueueItemType.Rematcher.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.Rematcher));
Intervals.Add(ProcessQueue.QueueItemType.Maintainer.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.Maintainer));
return Ok(Intervals);
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Route("Settings/BackgroundTasks/Intervals")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult SetBackgroundTasks(Dictionary<string, int> Intervals)
{
foreach (KeyValuePair<string, int> Interval in Intervals)
{
if (Enum.IsDefined(typeof(ProcessQueue.QueueItemType), Interval.Key))
{
try
{
BackgroundTaskItem taskItem = new BackgroundTaskItem(
(ProcessQueue.QueueItemType)Enum.Parse(typeof(ProcessQueue.QueueItemType), Interval.Key)
);
if (Interval.Value >= taskItem.MinimumAllowedValue)
{
Logging.Log(Logging.LogType.Information, "Update Background Task", "Updating task " + Interval.Key + " with new interval " + Interval.Value);
Config.SetSetting("Interval_" + Interval.Key, Interval.Value.ToString());
// update existing process
foreach (ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
{
if (item.ItemType.ToString().ToLower() == Interval.Key.ToLower())
{
item.Interval = Interval.Value;
}
}
}
else
{
Logging.Log(Logging.LogType.Warning, "Update Background Task", "Interval " + Interval.Value + " for task " + Interval.Key + " is below the minimum allowed value of " + taskItem.MinimumAllowedValue + ". Skipping.");
}
}
catch
{
// task name not defined
Logging.Log(Logging.LogType.Warning, "Update Background Task", "Task " + Interval.Key + " is not user definable. Skipping.");
}
}
}
return Ok();
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Settings/System")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetSystemSettings()
{
SystemSettingsModel systemSettingsModel = new SystemSettingsModel{
AlwaysLogToDisk = Config.LoggingConfiguration.AlwaysLogToDisk,
MinimumLogRetentionPeriod = Config.LoggingConfiguration.LogRetention
};
return Ok(systemSettingsModel);
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Route("Settings/System")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult SetSystemSettings(SystemSettingsModel model)
{
if (ModelState.IsValid)
{
Config.LoggingConfiguration.AlwaysLogToDisk = model.AlwaysLogToDisk;
Config.LoggingConfiguration.LogRetention = model.MinimumLogRetentionPeriod;
Config.UpdateConfig();
}
return Ok(model);
}
private SystemInfo.PathItem GetDisk(string Path)
{
SystemInfo.PathItem pathItem = new SystemInfo.PathItem {
LibraryPath = Path,
SpaceUsed = Common.DirSize(new DirectoryInfo(Path)),
SpaceAvailable = new DriveInfo(Path).AvailableFreeSpace,
TotalSpace = new DriveInfo(Path).TotalSize
};
return pathItem;
}
public class SystemInfo
{
public Version ApplicationVersion {
get
{
return Assembly.GetExecutingAssembly().GetName().Version;
}
}
public List<PathItem>? Paths { get; set; }
public long DatabaseSize { get; set; }
public List<PlatformStatisticsItem>? PlatformStatistics { get; set; }
public class PathItem
{
public string LibraryPath { get; set; }
public long SpaceUsed { get; set; }
public long SpaceAvailable { get; set; }
public long TotalSpace { get; set; }
}
public class PlatformStatisticsItem
{
public string Platform { get; set; }
public long RomCount { get; set; }
public long TotalSize { get; set; }
}
}
}
public class BackgroundTaskItem
{
public BackgroundTaskItem(ProcessQueue.QueueItemType TaskName)
{
this.Task = TaskName.ToString();
switch (TaskName)
{
case ProcessQueue.QueueItemType.SignatureIngestor:
this.DefaultInterval = 60;
this.MinimumAllowedValue = 20;
break;
case ProcessQueue.QueueItemType.TitleIngestor:
this.DefaultInterval = 1;
this.MinimumAllowedValue = 1;
break;
case ProcessQueue.QueueItemType.MetadataRefresh:
this.DefaultInterval = 360;
this.MinimumAllowedValue = 360;
break;
case ProcessQueue.QueueItemType.OrganiseLibrary:
this.DefaultInterval = 1440;
this.MinimumAllowedValue = 120;
break;
case ProcessQueue.QueueItemType.LibraryScan:
this.DefaultInterval = 1440;
this.MinimumAllowedValue = 120;
break;
case ProcessQueue.QueueItemType.Rematcher:
this.DefaultInterval = 1440;
this.MinimumAllowedValue = 360;
break;
case ProcessQueue.QueueItemType.Maintainer:
this.DefaultInterval = 10080;
this.MinimumAllowedValue = 10080;
break;
default:
throw new Exception("Invalid task");
}
}
public string Task { get; set; }
public int Interval {
get
{
return int.Parse(Config.ReadSetting("Interval_" + Task, DefaultInterval.ToString()));
}
}
public int DefaultInterval { get; set; }
public int MinimumAllowedValue { get; set; }
}
public class SystemSettingsModel
{
public bool AlwaysLogToDisk { get; set; }
public int MinimumLogRetentionPeriod { get; set; }
}
}

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Authentication;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
{
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.1")]
[Authorize]
public class FirstSetupController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public FirstSetupController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
[MapToApiVersion("1.1")]
[HttpPost]
[Route("0")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> CreateAdminAccount(Authentication.RegisterViewModel model)
{
if (Config.ReadSetting("FirstRunStatus", "0") == "0")
{
if (ModelState.IsValid)
{
ApplicationUser user = new ApplicationUser
{
UserName = model.UserName,
NormalizedUserName = model.UserName.ToUpper(),
Email = model.Email,
NormalizedEmail = model.Email.ToUpper(),
SecurityProfile = new SecurityProfileViewModel()
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _userManager.AddToRoleAsync(user, "Player");
await _userManager.AddToRoleAsync(user, "Gamer");
await _userManager.AddToRoleAsync(user, "Admin");
await _signInManager.SignInAsync(user, isPersistent: true);
Config.SetSetting("FirstRunStatus", "1");
return Ok(result);
}
else
{
return Ok(result);
}
}
return Problem(ModelState.ToString());
}
else
{
return NotFound();
}
}
}
}

View File

@@ -0,0 +1,504 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Authentication;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Scripting;
using static gaseous_server.Classes.Metadata.AgeRatings;
namespace gaseous_server.Controllers.v1_1
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.1")]
[ApiController]
[Authorize]
public class GamesController: ControllerBase
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public GamesController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
[MapToApiVersion("1.1")]
[HttpPost]
[ProducesResponseType(typeof(GameReturnPackage), StatusCodes.Status200OK)]
public async Task<IActionResult> Game_v1_1(GameSearchModel model, int pageNumber = 0, int pageSize = 0)
{
var user = await _userManager.GetUserAsync(User);
if (user != null)
{
// apply security profile filtering
if (model.GameAgeRating.AgeGroupings.Count == 0)
{
model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Adult);
model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Mature);
model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Teen);
model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Child);
model.GameAgeRating.IncludeUnrated = true;
}
List<AgeGroups.AgeRestrictionGroupings> RemoveAgeGroups = new List<AgeGroups.AgeRestrictionGroupings>();
switch (user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction)
{
case AgeGroups.AgeRestrictionGroupings.Adult:
break;
case AgeGroups.AgeRestrictionGroupings.Mature:
RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Adult);
break;
case AgeGroups.AgeRestrictionGroupings.Teen:
RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Adult);
RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Mature);
break;
case AgeGroups.AgeRestrictionGroupings.Child:
RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Adult);
RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Mature);
RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Teen);
break;
}
foreach (AgeGroups.AgeRestrictionGroupings RemoveAgeGroup in RemoveAgeGroups)
{
if (model.GameAgeRating.AgeGroupings.Contains(RemoveAgeGroup))
{
model.GameAgeRating.AgeGroupings.Remove(RemoveAgeGroup);
}
}
if (user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated == false)
{
model.GameAgeRating.IncludeUnrated = false;
}
return Ok(GetGames(model, pageNumber, pageSize));
}
else
{
return Unauthorized();
}
}
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/Related")]
[ProducesResponseType(typeof(GameReturnPackage), StatusCodes.Status200OK)]
public async Task<IActionResult> GameRelated(long GameId)
{
var user = await _userManager.GetUserAsync(User);
if (user != null)
{
string IncludeUnrated = "";
if (user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated == true) {
IncludeUnrated = " OR view_Games.AgeGroupId IS NULL";
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT view_Games.Id, view_Games.AgeGroupId, Relation_Game_SimilarGames.SimilarGamesId FROM view_Games JOIN Relation_Game_SimilarGames ON view_Games.Id = Relation_Game_SimilarGames.GameId AND Relation_Game_SimilarGames.SimilarGamesId IN (SELECT Id FROM view_Games) WHERE view_Games.Id = @id AND (view_Games.AgeGroupId <= @agegroupid" + IncludeUnrated + ")";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", GameId);
dbDict.Add("agegroupid", (int)user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction);
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
DataTable dbResponse = db.ExecuteCMD(sql, dbDict);
foreach (DataRow dr in dbResponse.Rows)
{
RetVal.Add(Classes.Metadata.Games.GetGame((long)dr["SimilarGamesId"], false, false, false));
}
GameReturnPackage gameReturn = new GameReturnPackage(RetVal.Count, RetVal);
return Ok(gameReturn);
}
else
{
return Unauthorized();
}
}
public class GameSearchModel
{
public string Name { get; set; }
public List<string> Platform { get; set; }
public List<string> Genre { get; set; }
public List<string> GameMode { get; set; }
public List<string> PlayerPerspective { get; set; }
public List<string> Theme { get; set; }
public GameRatingItem GameRating { get; set; } = new GameRatingItem();
public GameAgeRatingItem GameAgeRating { get; set; } = new GameAgeRatingItem();
public GameSortingItem Sorting { get; set; } = new GameSortingItem();
public class GameRatingItem
{
public int MinimumRating { get; set; } = -1;
public int MinimumRatingCount { get; set; } = -1;
public int MaximumRating { get; set; } = -1;
public int MaximumRatingCount { get; set; } = -1;
public bool IncludeUnrated { get; set; } = false;
}
public class GameAgeRatingItem
{
public List<AgeGroups.AgeRestrictionGroupings> AgeGroupings { get; set; } = new List<AgeGroups.AgeRestrictionGroupings>{
AgeGroups.AgeRestrictionGroupings.Child,
AgeGroups.AgeRestrictionGroupings.Teen,
AgeGroups.AgeRestrictionGroupings.Mature,
AgeGroups.AgeRestrictionGroupings.Adult
};
public bool IncludeUnrated { get; set; } = true;
}
public class GameSortingItem
{
public SortField SortBy { get; set; } = SortField.NameThe;
public bool SortAscending { get; set; } = true;
public enum SortField
{
Name,
NameThe,
Rating,
RatingCount
}
}
}
public static GameReturnPackage GetGames(GameSearchModel model, int pageNumber = 0, int pageSize = 0)
{
string whereClause = "";
string havingClause = "";
Dictionary<string, object> whereParams = new Dictionary<string, object>();
List<string> whereClauses = new List<string>();
List<string> havingClauses = new List<string>();
string tempVal = "";
if (model.Name.Length > 0)
{
tempVal = "`Name` LIKE @Name";
whereParams.Add("@Name", "%" + model.Name + "%");
havingClauses.Add(tempVal);
}
if (model.GameRating != null)
{
List<string> ratingClauses = new List<string>();
if (model.GameRating.MinimumRating != -1)
{
string ratingTempMinVal = "totalRating >= @totalMinRating";
whereParams.Add("@totalMinRating", model.GameRating.MinimumRating);
ratingClauses.Add(ratingTempMinVal);
}
if (model.GameRating.MaximumRating != -1)
{
string ratingTempMaxVal = "totalRating <= @totalMaxRating";
whereParams.Add("@totalMaxRating", model.GameRating.MaximumRating);
ratingClauses.Add(ratingTempMaxVal);
}
if (model.GameRating.MinimumRatingCount != -1)
{
string ratingTempMinCountVal = "totalRatingCount >= @totalMinRatingCount";
whereParams.Add("@totalMinRatingCount", model.GameRating.MinimumRatingCount);
ratingClauses.Add(ratingTempMinCountVal);
}
if (model.GameRating.MaximumRatingCount != -1)
{
string ratingTempMaxCountVal = "totalRatingCount <= @totalMaxRatingCount";
whereParams.Add("@totalMaxRatingCount", model.GameRating.MaximumRatingCount);
ratingClauses.Add(ratingTempMaxCountVal);
}
// generate rating sub clause
string ratingClauseValue = "";
if (ratingClauses.Count > 0)
{
foreach (string ratingClause in ratingClauses)
{
if (ratingClauseValue.Length > 0)
{
ratingClauseValue += " AND ";
}
ratingClauseValue += ratingClause;
}
}
string unratedClause = "";
if (model.GameRating.IncludeUnrated == true)
{
unratedClause = "totalRating IS NULL";
}
if (ratingClauseValue.Length > 0)
{
if (unratedClause.Length > 0)
{
havingClauses.Add("((" + ratingClauseValue + ") OR " + unratedClause + ")");
}
else
{
havingClauses.Add("(" + ratingClauseValue + ")");
}
}
}
if (model.Platform.Count > 0)
{
tempVal = "Games_Roms.PlatformId IN (";
for (int i = 0; i < model.Platform.Count; i++)
{
if (i > 0)
{
tempVal += ", ";
}
string platformLabel = "@Platform" + i;
tempVal += platformLabel;
whereParams.Add(platformLabel, model.Platform[i]);
}
tempVal += ")";
whereClauses.Add(tempVal);
}
if (model.Genre.Count > 0)
{
tempVal = "Relation_Game_Genres.GenresId IN (";
for (int i = 0; i < model.Genre.Count; i++)
{
if (i > 0)
{
tempVal += " AND ";
}
string genreLabel = "@Genre" + i;
tempVal += genreLabel;
whereParams.Add(genreLabel, model.Genre[i]);
}
tempVal += ")";
whereClauses.Add(tempVal);
}
if (model.GameMode.Count > 0)
{
tempVal = "Relation_Game_GameModes.GameModesId IN (";
for (int i = 0; i < model.GameMode.Count; i++)
{
if (i > 0)
{
tempVal += " AND ";
}
string gameModeLabel = "@GameMode" + i;
tempVal += gameModeLabel;
whereParams.Add(gameModeLabel, model.GameMode[i]);
}
tempVal += ")";
whereClauses.Add(tempVal);
}
if (model.PlayerPerspective.Count > 0)
{
tempVal = "Relation_Game_PlayerPerspectives.PlayerPerspectivesId IN (";
for (int i = 0; i < model.PlayerPerspective.Count; i++)
{
if (i > 0)
{
tempVal += " AND ";
}
string playerPerspectiveLabel = "@PlayerPerspective" + i;
tempVal += playerPerspectiveLabel;
whereParams.Add(playerPerspectiveLabel, model.PlayerPerspective[i]);
}
tempVal += ")";
whereClauses.Add(tempVal);
}
if (model.Theme.Count > 0)
{
tempVal = "Relation_Game_Themes.ThemesId IN (";
for (int i = 0; i < model.Theme.Count; i++)
{
if (i > 0)
{
tempVal += " AND ";
}
string themeLabel = "@Theme" + i;
tempVal += themeLabel;
whereParams.Add(themeLabel, model.Theme[i]);
}
tempVal += ")";
whereClauses.Add(tempVal);
}
if (model.GameAgeRating != null)
{
if (model.GameAgeRating.AgeGroupings.Count > 0)
{
tempVal = "(AgeGroupId IN (";
for (int i = 0; i < model.GameAgeRating.AgeGroupings.Count; i++)
{
if (i > 0)
{
tempVal += ", ";
}
string themeLabel = "@Rating" + i;
tempVal += themeLabel;
whereParams.Add(themeLabel, model.GameAgeRating.AgeGroupings[i]);
}
tempVal += ")";
if (model.GameAgeRating.IncludeUnrated == true)
{
tempVal += " OR AgeGroupId IS NULL";
}
tempVal += ")";
whereClauses.Add(tempVal);
}
}
// build where clause
if (whereClauses.Count > 0)
{
whereClause = "WHERE ";
for (int i = 0; i < whereClauses.Count; i++)
{
if (i > 0)
{
whereClause += " AND ";
}
whereClause += whereClauses[i];
}
}
// build having clause
if (havingClauses.Count > 0)
{
havingClause = "HAVING ";
for (int i = 0; i < havingClauses.Count; i++)
{
if (i > 0)
{
havingClause += " AND ";
}
havingClause += havingClauses[i];
}
}
// order by clause
string orderByField = "NameThe";
string orderByOrder = "ASC";
if (model.Sorting != null)
{
switch(model.Sorting.SortBy)
{
case GameSearchModel.GameSortingItem.SortField.NameThe:
orderByField = "NameThe";
break;
case GameSearchModel.GameSortingItem.SortField.Name:
orderByField = "Name";
break;
case GameSearchModel.GameSortingItem.SortField.Rating:
orderByField = "TotalRating";
break;
case GameSearchModel.GameSortingItem.SortField.RatingCount:
orderByField = "TotalRatingCount";
break;
default:
orderByField = "NameThe";
break;
}
if (model.Sorting.SortAscending == true)
{
orderByOrder = "ASC";
}
else
{
orderByOrder = "DESC";
}
}
string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT DISTINCT view_Games.* FROM view_Games LEFT JOIN Games_Roms ON view_Games.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON view_Games.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON view_Games.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON view_Games.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON view_Games.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
DataTable dbResponse = db.ExecuteCMD(sql, whereParams);
// get count
int RecordCount = dbResponse.Rows.Count;
// compile data for return
int pageOffset = pageSize * (pageNumber - 1);
for (int i = 0; i < dbResponse.Rows.Count; i++)
{
DataRow dr = dbResponse.Rows[i];
bool includeGame = false;
if (pageSize == 0)
{
// page size is full size include all
includeGame = true;
}
else if (i >= pageOffset && i < (pageOffset + pageSize))
{
includeGame = true;
}
if (includeGame == true)
{
RetVal.Add(Classes.Metadata.Games.GetGame(dr));
}
}
GameReturnPackage gameReturn = new GameReturnPackage(RecordCount, RetVal);
return gameReturn;
}
public class GameReturnPackage
{
public GameReturnPackage()
{
}
public GameReturnPackage(int Count, List<Game> Games)
{
this.Count = Count;
List<Games.MinimalGameItem> minimalGames = new List<Games.MinimalGameItem>();
foreach (Game game in Games)
{
minimalGames.Add(new Classes.Metadata.Games.MinimalGameItem(game));
}
this.Games = minimalGames;
}
public int Count { get; set; }
public List<Games.MinimalGameItem> Games { get; set; } = new List<Games.MinimalGameItem>();
}
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting;
using static gaseous_server.Classes.Metadata.AgeRatings;
namespace gaseous_server.Controllers.v1_1
{
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.1")]
[ApiController]
public class RatingsController: ControllerBase
{
[MapToApiVersion("1.1")]
[HttpGet]
[Authorize]
[Route("Images/{RatingBoard}/{RatingId}/image.svg")]
[ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult RatingsImageById(string RatingBoard, int RatingId)
{
IGDB.Models.AgeRatingTitle RatingTitle = (AgeRatingTitle)RatingId;
string resourceName = "gaseous_server.Assets.Ratings." + RatingBoard + "." + RatingTitle.ToString() + ".svg";
var assembly = Assembly.GetExecutingAssembly();
string[] resources = assembly.GetManifestResourceNames();
if (resources.Contains(resourceName))
{
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
byte[] filedata = new byte[stream.Length];
stream.Read(filedata, 0, filedata.Length);
string filename = RatingBoard + "-" + RatingTitle.ToString() + ".svg";
string contentType = "image/svg+xml";
var cd = new System.Net.Mime.ContentDisposition
{
FileName = filename,
Inline = true,
};
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("Cache-Control", "public, max-age=604800");
return File(filedata, contentType);
}
}
return NotFound();
}
}
}

View File

@@ -4,10 +4,10 @@ using System.Data;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Web;
using gaseous_server.Classes; using gaseous_server.Classes;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_server.Controllers; using gaseous_server.Controllers;
using gaseous_tools;
using IGDB.Models; using IGDB.Models;
using Newtonsoft.Json; using Newtonsoft.Json;
@@ -72,6 +72,9 @@ namespace gaseous_server.Models
foreach (PlatformMapItem mapItem in platforms) foreach (PlatformMapItem mapItem in platforms)
{ {
// get the IGDB platform data
Platform platform = Platforms.GetPlatform(mapItem.IGDBId);
try try
{ {
PlatformMapItem item = GetPlatformMap(mapItem.IGDBId); PlatformMapItem item = GetPlatformMap(mapItem.IGDBId);
@@ -93,7 +96,7 @@ namespace gaseous_server.Models
{ {
get get
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM PlatformMap"; string sql = "SELECT * FROM PlatformMap";
DataTable data = db.ExecuteCMD(sql); DataTable data = db.ExecuteCMD(sql);
@@ -125,7 +128,7 @@ namespace gaseous_server.Models
} }
else else
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM PlatformMap WHERE Id = @Id"; string sql = "SELECT * FROM PlatformMap WHERE Id = @Id";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Id", Id); dbDict.Add("Id", Id);
@@ -148,7 +151,7 @@ namespace gaseous_server.Models
public static void WritePlatformMap(PlatformMapItem item, bool Update, bool AllowAvailableEmulatorOverwrite) public static void WritePlatformMap(PlatformMapItem item, bool Update, bool AllowAvailableEmulatorOverwrite)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
if (Update == false) if (Update == false)
@@ -198,7 +201,7 @@ namespace gaseous_server.Models
sql = "INSERT INTO PlatformMap_AlternateNames (Id, Name) VALUES (@Id, @Name);"; sql = "INSERT INTO PlatformMap_AlternateNames (Id, Name) VALUES (@Id, @Name);";
dbDict.Clear(); dbDict.Clear();
dbDict.Add("Id", item.IGDBId); dbDict.Add("Id", item.IGDBId);
dbDict.Add("Name", alternateName); dbDict.Add("Name", HttpUtility.HtmlDecode(alternateName));
db.ExecuteCMD(sql, dbDict); db.ExecuteCMD(sql, dbDict);
} }
} }
@@ -245,7 +248,7 @@ namespace gaseous_server.Models
static PlatformMapItem BuildPlatformMapItem(DataRow row) static PlatformMapItem BuildPlatformMapItem(DataRow row)
{ {
long IGDBId = (long)row["Id"]; long IGDBId = (long)row["Id"];
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
string sql = ""; string sql = "";

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using gaseous_signature_parser.models.RomSignatureObject;
namespace gaseous_server.Models namespace gaseous_server.Models
{ {
@@ -128,49 +129,11 @@ namespace gaseous_server.Models
public List<KeyValuePair<string, object>> Attributes { get; set; } = new List<KeyValuePair<string, object>>(); public List<KeyValuePair<string, object>> Attributes { get; set; } = new List<KeyValuePair<string, object>>();
public RomTypes RomType { get; set; } public RomSignatureObject.Game.Rom.RomTypes RomType { get; set; }
public string? RomTypeMedia { get; set; } public string? RomTypeMedia { get; set; }
public string? MediaLabel { get; set; } public string? MediaLabel { get; set; }
public gaseous_signature_parser.models.RomSignatureObject.RomSignatureObject.Game.Rom.SignatureSourceType SignatureSource { get; set; } public RomSignatureObject.Game.Rom.SignatureSourceType SignatureSource { get; set; }
public enum RomTypes
{
/// <summary>
/// Media type is unknown
/// </summary>
Unknown = 0,
/// <summary>
/// Optical media
/// </summary>
Disc = 1,
/// <summary>
/// Magnetic media
/// </summary>
Disk = 2,
/// <summary>
/// Individual files
/// </summary>
File = 3,
/// <summary>
/// Individual pars
/// </summary>
Part = 4,
/// <summary>
/// Tape base media
/// </summary>
Tape = 5,
/// <summary>
/// Side of the media
/// </summary>
Side = 6
}
[JsonIgnore] [JsonIgnore]
public int Score public int Score

View File

@@ -1,6 +1,6 @@
using System; using System;
using System.Data; using System.Data;
using gaseous_tools; using gaseous_server.Classes;
namespace gaseous_server.Models namespace gaseous_server.Models
{ {
@@ -14,7 +14,7 @@ namespace gaseous_server.Models
public Signatures_Status() public Signatures_Status()
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "select (select count(*) from Signatures_Sources) as SourceCount, (select count(*) from Signatures_Platforms) as PlatformCount, (select count(*) from Signatures_Games) as GameCount, (select count(*) from Signatures_Roms) as RomCount;"; string sql = "select (select count(*) from Signatures_Sources) as SourceCount, (select count(*) from Signatures_Platforms) as PlatformCount, (select count(*) from Signatures_Games) as GameCount, (select count(*) from Signatures_Roms) as RomCount;";
DataTable sigDb = db.ExecuteCMD(sql); DataTable sigDb = db.ExecuteCMD(sql);

View File

@@ -1,5 +1,5 @@
using System; using System;
using gaseous_tools; using gaseous_server.Classes;
namespace gaseous_server namespace gaseous_server
{ {
@@ -67,7 +67,17 @@ namespace gaseous_server
return LastRunTime.AddMinutes(Interval); return LastRunTime.AddMinutes(Interval);
} }
} }
public int Interval => _Interval; public int Interval
{
get
{
return _Interval;
}
set
{
_Interval = value;
}
}
public string LastResult => _LastResult; public string LastResult => _LastResult;
public string? LastError => _LastError; public string? LastError => _LastError;
public bool Force => _ForceExecute; public bool Force => _ForceExecute;
@@ -116,13 +126,15 @@ namespace gaseous_server
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Title Ingestor"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Title Ingestor");
Classes.ImportGames importGames = new Classes.ImportGames(Config.LibraryConfiguration.LibraryImportDirectory); Classes.ImportGames importGames = new Classes.ImportGames(Config.LibraryConfiguration.LibraryImportDirectory);
Classes.ImportGame.DeleteOrphanedDirectories(Config.LibraryConfiguration.LibraryImportDirectory);
_SaveLastRunTime = true; _SaveLastRunTime = true;
break; break;
case QueueItemType.MetadataRefresh: case QueueItemType.MetadataRefresh:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Metadata Refresher"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Metadata Refresher");
Classes.MetadataManagement.RefreshMetadata(true); Classes.MetadataManagement.RefreshMetadata(_ForceExecute);
_SaveLastRunTime = true; _SaveLastRunTime = true;
@@ -144,14 +156,32 @@ namespace gaseous_server
break; break;
case QueueItemType.Rematcher:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Rematch");
Classes.ImportGame.Rematcher(_ForceExecute);
_SaveLastRunTime = true;
break;
case QueueItemType.CollectionCompiler: case QueueItemType.CollectionCompiler:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Collection Compiler"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Collection Compiler");
Classes.Collections.CompileCollections((long)Options); Classes.Collections.CompileCollections((long)Options);
break; break;
case QueueItemType.MediaGroupCompiler:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Media Group Compiler");
Classes.RomMediaGroup.CompileMediaGroup((long)Options);
break;
case QueueItemType.BackgroundDatabaseUpgrade: case QueueItemType.BackgroundDatabaseUpgrade:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Background Upgrade"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Background Upgrade");
gaseous_tools.DatabaseMigration.UpgradeScriptBackgroundTasks(); DatabaseMigration.UpgradeScriptBackgroundTasks();
break;
case QueueItemType.Maintainer:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Maintenance");
Classes.Maintenance.RunMaintenance();
break; break;
} }
@@ -166,6 +196,8 @@ namespace gaseous_server
_ForceExecute = false; _ForceExecute = false;
_ItemState = QueueItemState.Stopped; _ItemState = QueueItemState.Stopped;
_LastFinishTime = DateTime.UtcNow; _LastFinishTime = DateTime.UtcNow;
Logging.Log(Logging.LogType.Information, "Timered Event", "Total " + _ItemType + " run time = " + (DateTime.UtcNow - _LastRunTime).TotalSeconds);
} }
} }
} }
@@ -218,15 +250,30 @@ namespace gaseous_server
/// </summary> /// </summary>
LibraryScan, LibraryScan,
/// <summary>
/// Looks for roms in the library that have an unknown platform or game match
/// </summary>
Rematcher,
/// <summary> /// <summary>
/// Builds collections - set the options attribute to the id of the collection to build /// Builds collections - set the options attribute to the id of the collection to build
/// </summary> /// </summary>
CollectionCompiler, CollectionCompiler,
/// <summary>
/// Builds media groups - set the options attribute to the id of the media group to build
/// </summary>
MediaGroupCompiler,
/// <summary> /// <summary>
/// Performs and post database upgrade scripts that can be processed as a background task /// Performs and post database upgrade scripts that can be processed as a background task
/// </summary> /// </summary>
BackgroundDatabaseUpgrade BackgroundDatabaseUpgrade,
/// <summary>
/// Performs a clean up of old files, and optimises the database
/// </summary>
Maintainer
} }
public enum QueueItemState public enum QueueItemState

View File

@@ -1,18 +1,25 @@
using System.Reflection; using System.Reflection;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using gaseous_server; using gaseous_server;
using gaseous_server.Classes;
using gaseous_server.Models; using gaseous_server.Models;
using gaseous_server.SignatureIngestors.XML; using gaseous_server.SignatureIngestors.XML;
using gaseous_tools;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using IGDB.Models;
using gaseous_server.Classes.Metadata;
Logging.WriteToDiskOnly = true; Logging.WriteToDiskOnly = true;
Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server " + Assembly.GetExecutingAssembly().GetName().Version); Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server " + Assembly.GetExecutingAssembly().GetName().Version);
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionStringNoDatabase);
// check db availability // check db availability
bool dbOnline = false; bool dbOnline = false;
@@ -27,11 +34,16 @@ do
{ {
Thread.Sleep(30000); Thread.Sleep(30000);
} }
} while (dbOnline == true); } while (dbOnline == false);
db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
// set up db // set up db
db.InitDB(); db.InitDB();
// populate db with static data for lookups
AgeRatings.PopulateAgeMap();
// load app settings // load app settings
Config.InitSettings(); Config.InitSettings();
// write updated settings back to the config file // write updated settings back to the config file
@@ -46,6 +58,16 @@ if (Config.ReadSetting("API Key", "Test API Key") == "Test API Key")
Config.SetSetting("API Key", APIKey.ToString()); Config.SetSetting("API Key", APIKey.ToString());
} }
// clean up storage
if (Directory.Exists(Config.LibraryConfiguration.LibraryTempDirectory))
{
Directory.Delete(Config.LibraryConfiguration.LibraryTempDirectory, true);
}
if (Directory.Exists(Config.LibraryConfiguration.LibraryUploadDirectory))
{
Directory.Delete(Config.LibraryConfiguration.LibraryUploadDirectory, true);
}
// kick off any delayed upgrade tasks // kick off any delayed upgrade tasks
// run 1002 background updates in the background on every start // run 1002 background updates in the background on every start
DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1002); DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1002);
@@ -99,6 +121,24 @@ builder.Services.AddControllers(options =>
Location = ResponseCacheLocation.Any Location = ResponseCacheLocation.Any
}); });
}); });
builder.Services.AddApiVersioning(config =>
{
config.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
config.ReportApiVersions = true;
config.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("x-api-version"),
new MediaTypeApiVersionReader("x-api-version"));
});
// builder.Services.AddApiVersioning(setup =>
// {
// setup.ApiVersionReader = new UrlSegmentApiVersionReader();
// });
builder.Services.AddVersionedApiExplorer(setup =>
{
setup.GroupNameFormat = "'v'VVV";
setup.SubstituteApiVersionInUrl = true;
});
// set max upload size // set max upload size
builder.Services.Configure<IISServerOptions>(options => builder.Services.Configure<IISServerOptions>(options =>
@@ -122,7 +162,25 @@ builder.Services.AddSwaggerGen(options =>
{ {
options.SwaggerDoc("v1", new OpenApiInfo options.SwaggerDoc("v1", new OpenApiInfo
{ {
Version = "v1", Version = "v1.0",
Title = "Gaseous Server API",
Description = "An API for managing the Gaseous Server",
TermsOfService = new Uri("https://github.com/gaseous-project/gaseous-server"),
Contact = new OpenApiContact
{
Name = "GitHub Repository",
Url = new Uri("https://github.com/gaseous-project/gaseous-server")
},
License = new OpenApiLicense
{
Name = "Gaseous Server License",
Url = new Uri("https://github.com/gaseous-project/gaseous-server/blob/main/LICENSE")
}
});
options.SwaggerDoc("v1.1", new OpenApiInfo
{
Version = "v1.1",
Title = "Gaseous Server API", Title = "Gaseous Server API",
Description = "An API for managing the Gaseous Server", Description = "An API for managing the Gaseous Server",
TermsOfService = new Uri("https://github.com/gaseous-project/gaseous-server"), TermsOfService = new Uri("https://github.com/gaseous-project/gaseous-server"),
@@ -145,19 +203,118 @@ builder.Services.AddSwaggerGen(options =>
); );
builder.Services.AddHostedService<TimedHostedService>(); builder.Services.AddHostedService<TimedHostedService>();
// identity
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 10;
options.User.AllowedUserNameCharacters = null;
options.User.RequireUniqueEmail = true;
options.SignIn.RequireConfirmedPhoneNumber = false;
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedAccount = false;
})
.AddUserStore<UserStore>()
.AddRoleStore<RoleStore>()
.AddDefaultTokenProviders()
.AddDefaultUI()
;
builder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "Gaseous.Identity";
options.ExpireTimeSpan = TimeSpan.FromDays(90);
options.SlidingExpiration = true;
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.SameSite = SameSiteMode.Strict;
});
// builder.Services.AddIdentityCore<ApplicationUser>(options => {
// options.SignIn.RequireConfirmedAccount = false;
// options.User.RequireUniqueEmail = true;
// options.Password.RequireDigit = false;
// options.Password.RequiredLength = 10;
// options.Password.RequireNonAlphanumeric = false;
// options.Password.RequireUppercase = false;
// options.Password.RequireLowercase = false;
// });
builder.Services.AddScoped<UserStore>();
builder.Services.AddScoped<RoleStore>();
builder.Services.AddTransient<IUserStore<ApplicationUser>, UserStore>();
builder.Services.AddTransient<IRoleStore<ApplicationRole>, RoleStore>();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy => policy.RequireRole("Admin"));
options.AddPolicy("Gamer", policy => policy.RequireRole("Gamer"));
options.AddPolicy("Player", policy => policy.RequireRole("Player"));
});
// builder.Services.AddControllersWithViews(options =>
// {
// options.Filters.Add(new Microsoft.AspNetCore.Mvc.ValidateAntiForgeryTokenAttribute());
// });
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment()) //if (app.Environment.IsDevelopment())
//{ //{
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(); app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint($"/swagger/v1/swagger.json", "v1.0");
options.SwaggerEndpoint($"/swagger/v1.1/swagger.json", "v1.1");
}
);
//} //}
//app.UseHttpsRedirection(); //app.UseHttpsRedirection();
app.UseResponseCaching(); app.UseResponseCaching();
// set up system roles
using (var scope = app.Services.CreateScope())
{
var roleManager = scope.ServiceProvider.GetRequiredService<RoleStore>();
var roles = new[] { "Admin", "Gamer", "Player" };
foreach (var role in roles)
{
if (await roleManager.FindByNameAsync(role, CancellationToken.None) == null)
{
ApplicationRole applicationRole = new ApplicationRole();
applicationRole.Name = role;
applicationRole.NormalizedName = role.ToUpper();
await roleManager.CreateAsync(applicationRole, CancellationToken.None);
}
}
// // set up administrator account
// var userManager = scope.ServiceProvider.GetRequiredService<UserStore>();
// if (await userManager.FindByNameAsync("admin@localhost", CancellationToken.None) == null)
// {
// ApplicationUser adminUser = new ApplicationUser{
// Id = Guid.NewGuid().ToString(),
// Email = "admin@localhost",
// NormalizedEmail = "ADMIN@LOCALHOST",
// EmailConfirmed = true,
// UserName = "administrator",
// NormalizedUserName = "ADMINISTRATOR"
// };
// //set user password
// PasswordHasher<ApplicationUser> ph = new PasswordHasher<ApplicationUser>();
// adminUser.PasswordHash = ph.HashPassword(adminUser, "letmein");
// await userManager.CreateAsync(adminUser, CancellationToken.None);
// await userManager.AddToRoleAsync(adminUser, "Admin", CancellationToken.None);
// }
}
app.UseAuthorization(); app.UseAuthorization();
app.UseDefaultFiles(); app.UseDefaultFiles();
@@ -169,6 +326,72 @@ app.UseStaticFiles(new StaticFileOptions
app.MapControllers(); app.MapControllers();
// emergency password recovery if environment variable is set
// process:
// - set the environment variable "recoveraccount" to the email address of the account to be recovered
// - when the server starts the password will be reset to a random string and saved in the library
// directory with the name RecoverAccount.txt
// - user should copy this password and remove the "recoveraccount" environment variable and the
// RecoverAccount.txt file
// - the server will not start while the RecoverAccount.txt file exists
string PasswordRecoveryFile = Path.Combine(Config.LibraryConfiguration.LibraryRootDirectory, "RecoverAccount.txt");
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("recoveraccount")))
{
if (File.Exists(PasswordRecoveryFile))
{
// password has already been set - do nothing and just exit
Logging.Log(Logging.LogType.Critical, "Server Startup", "Unable to start while recoveraccount environment varibale is set and RecoverAccount.txt file exists.", null, true);
Environment.Exit(0);
}
else
{
// generate and save the password to disk
int length = 10;
string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+";
var random = new Random();
string password = new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
File.WriteAllText(PasswordRecoveryFile, password);
// reset the password
using (var scope = app.Services.CreateScope())
{
var userManager = scope.ServiceProvider.GetRequiredService<UserStore>();
if (await userManager.FindByNameAsync(Environment.GetEnvironmentVariable("recoveraccount"), CancellationToken.None) != null)
{
ApplicationUser User = await userManager.FindByEmailAsync(Environment.GetEnvironmentVariable("recoveraccount"), CancellationToken.None);
//set user password
PasswordHasher<ApplicationUser> ph = new PasswordHasher<ApplicationUser>();
User.PasswordHash = ph.HashPassword(User, password);
await userManager.SetPasswordHashAsync(User, User.PasswordHash, CancellationToken.None);
Logging.Log(Logging.LogType.Information, "Server Startup", "Password reset complete, remove the recoveraccount environment variable and RecoverAccount.text file to allow server start.", null, true);
Environment.Exit(0);
}
else
{
Logging.Log(Logging.LogType.Critical, "Server Startup", "Account to recover not found.", null, true);
Environment.Exit(0);
}
}
}
}
else
{
// check if RecoverAccount.text file is present
if (File.Exists(PasswordRecoveryFile))
{
// cannot start while password recovery file exists
Logging.Log(Logging.LogType.Critical, "Server Startup", "Unable to start while RecoverAccount.txt file exists. Remove the file and try again.", null, true);
Environment.Exit(0);
}
}
// setup library directories // setup library directories
Config.LibraryConfiguration.InitLibrary(); Config.LibraryConfiguration.InitLibrary();
@@ -179,28 +402,63 @@ gaseous_server.Classes.Metadata.Platforms.GetPlatform(0);
// extract platform map if not present // extract platform map if not present
PlatformMapping.ExtractPlatformMap(); PlatformMapping.ExtractPlatformMap();
// force load platform map into cache
var platformMap = PlatformMapping.PlatformMap;
// add background tasks // add background tasks
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(ProcessQueue.QueueItemType.SignatureIngestor, 60));
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.TitleIngestor, 1, ProcessQueue.QueueItemType.SignatureIngestor,
int.Parse(Config.ReadSetting("Interval_SignatureIngestor", "60"))
)
);
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.TitleIngestor,
int.Parse(Config.ReadSetting("Interval_TitleIngestor", "1")),
new List<ProcessQueue.QueueItemType> new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.OrganiseLibrary, ProcessQueue.QueueItemType.OrganiseLibrary,
ProcessQueue.QueueItemType.LibraryScan ProcessQueue.QueueItemType.LibraryScan
}) })
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(ProcessQueue.QueueItemType.MetadataRefresh, 360));
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.OrganiseLibrary, 1440, new List<ProcessQueue.QueueItemType> ProcessQueue.QueueItemType.MetadataRefresh,
int.Parse(Config.ReadSetting("Interval_MetadataRefresh", "360"))
)
);
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.OrganiseLibrary,
int.Parse(Config.ReadSetting("Interval_OrganiseLibrary", "1440")),
new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.LibraryScan, ProcessQueue.QueueItemType.LibraryScan,
ProcessQueue.QueueItemType.TitleIngestor ProcessQueue.QueueItemType.TitleIngestor,
ProcessQueue.QueueItemType.Rematcher
}) })
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.LibraryScan, 1440, new List<ProcessQueue.QueueItemType> ProcessQueue.QueueItemType.LibraryScan,
int.Parse(Config.ReadSetting("Interval_LibraryScan", "1440")),
new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.OrganiseLibrary ProcessQueue.QueueItemType.OrganiseLibrary,
ProcessQueue.QueueItemType.Rematcher
})
);
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.Rematcher,
int.Parse(Config.ReadSetting("Interval_Rematcher", "1440")),
new List<ProcessQueue.QueueItemType>
{
ProcessQueue.QueueItemType.OrganiseLibrary,
ProcessQueue.QueueItemType.LibraryScan
})
);
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.Maintainer,
int.Parse(Config.ReadSetting("Interval_Maintainer", "10080")),
new List<ProcessQueue.QueueItemType>
{
ProcessQueue.QueueItemType.All
}) })
); );

Some files were not shown because too many files have changed in this diff Show More