diff --git a/NuGet.config b/NuGet.config
new file mode 100644
index 0000000..66e7ddd
--- /dev/null
+++ b/NuGet.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.idea/.idea.Kynareth/.idea/.gitignore b/src/.idea/.idea.Kynareth/.idea/.gitignore
new file mode 100644
index 0000000..a02bb95
--- /dev/null
+++ b/src/.idea/.idea.Kynareth/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/contentModel.xml
+/projectSettingsUpdater.xml
+/.idea.Kynareth.iml
+/modules.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/src/.idea/.idea.Kynareth/.idea/.name b/src/.idea/.idea.Kynareth/.idea/.name
new file mode 100644
index 0000000..09d1032
--- /dev/null
+++ b/src/.idea/.idea.Kynareth/.idea/.name
@@ -0,0 +1 @@
+Kynareth
\ No newline at end of file
diff --git a/src/.idea/.idea.Kynareth/.idea/efCoreCommonOptions.xml b/src/.idea/.idea.Kynareth/.idea/efCoreCommonOptions.xml
new file mode 100644
index 0000000..84f5122
--- /dev/null
+++ b/src/.idea/.idea.Kynareth/.idea/efCoreCommonOptions.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/.idea.Kynareth/.idea/efCoreDialogsState.xml b/src/.idea/.idea.Kynareth/.idea/efCoreDialogsState.xml
new file mode 100644
index 0000000..cb1c70d
--- /dev/null
+++ b/src/.idea/.idea.Kynareth/.idea/efCoreDialogsState.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/.idea.Kynareth/.idea/indexLayout.xml b/src/.idea/.idea.Kynareth/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/src/.idea/.idea.Kynareth/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/.idea.Kynareth/.idea/vcs.xml b/src/.idea/.idea.Kynareth/.idea/vcs.xml
new file mode 100644
index 0000000..64713b8
--- /dev/null
+++ b/src/.idea/.idea.Kynareth/.idea/vcs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/config/applicationhost.config b/src/.idea/config/applicationhost.config
new file mode 100644
index 0000000..6dbfa84
--- /dev/null
+++ b/src/.idea/config/applicationhost.config
@@ -0,0 +1,997 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Attributes/EndpointMetrics.cs b/src/Attributes/EndpointMetrics.cs
new file mode 100644
index 0000000..6ac9e7c
--- /dev/null
+++ b/src/Attributes/EndpointMetrics.cs
@@ -0,0 +1,39 @@
+using Exsersewo.Common.Extensions;
+using Kynareth.Helpers;
+using Kynareth.Models;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.Extensions.Primitives;
+
+namespace Kynareth.Attributes;
+
+public class EndpointMetricsAttribute : ActionFilterAttribute
+{
+ public string EndpointMetricName;
+
+ public EndpointMetricsAttribute(string metricName)
+ {
+ EndpointMetricName = metricName;
+ }
+
+ public override void OnActionExecuted(ActionExecutedContext context)
+ {
+ base.OnActionExecuted(context);
+
+ string key = context.HttpContext.Request.GetToken();
+
+ if (string.IsNullOrWhiteSpace(key)) return;
+
+ using var database = context.HttpContext.RequestServices.GetRequiredService();
+
+ var token = database.Tokens.FirstOrDefault(x => x.Token.Equals(key));
+
+ if (token is null) return;
+
+ token.ApiCalls.Add(new() {
+ TimeStamp = DateTime.UtcNow,
+ Route = EndpointMetricName
+ });
+
+ database.SaveChanges();
+ }
+}
\ No newline at end of file
diff --git a/src/Attributes/RequireTokenAttribute.cs b/src/Attributes/RequireTokenAttribute.cs
new file mode 100644
index 0000000..15f3be7
--- /dev/null
+++ b/src/Attributes/RequireTokenAttribute.cs
@@ -0,0 +1,26 @@
+using System.Net;
+using Exsersewo.Common.Utilities;
+using Kynareth.Helpers;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace Kynareth.Attributes;
+
+[AttributeUsage(AttributeTargets.Class)]
+public class RequireTokenAttribute : ActionFilterAttribute
+{
+ public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
+ {
+ if (await RequestHelper.IsRequestAuthenticatedAsync(context.HttpContext.Request))
+ {
+ await next();
+ return;
+ }
+
+ context.HttpContext.Response.ContentType = "application/json";
+ context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
+
+ var invalidRequest = EventResult.FromFailure("Unauthorized request");
+ await context.HttpContext.Response.WriteAsync(invalidRequest.ToJson());
+ return;
+ }
+}
\ No newline at end of file
diff --git a/src/Controllers/BaseController.cs b/src/Controllers/BaseController.cs
new file mode 100644
index 0000000..e9525d3
--- /dev/null
+++ b/src/Controllers/BaseController.cs
@@ -0,0 +1,22 @@
+using Kynareth.Attributes;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Kynareth.Controllers;
+
+[ApiController]
+[RequireToken]
+public class BaseController : ControllerBase
+{
+ protected readonly IWebHostEnvironment _appEnvironment;
+
+ protected readonly ILogger _logger;
+
+ protected readonly IConfiguration _configuration;
+
+ public BaseController(IConfiguration configuration, ILoggerFactory loggerFactory, IWebHostEnvironment appEnvironment) : base()
+ {
+ _configuration = configuration;
+ _appEnvironment = appEnvironment;
+ _logger = loggerFactory.CreateLogger();
+ }
+}
\ No newline at end of file
diff --git a/src/Controllers/ImageGenerationController.cs b/src/Controllers/ImageGenerationController.cs
new file mode 100644
index 0000000..0ce57f6
--- /dev/null
+++ b/src/Controllers/ImageGenerationController.cs
@@ -0,0 +1,53 @@
+using System.Text.Json;
+using Exsersewo.Common.Utilities;
+using ImageMagick;
+using Kynareth.Attributes;
+using Kynareth.Helpers;
+using Kynareth.Managers;
+using Kynareth.Models;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Kynareth.Controllers;
+
+[Route("image/generate")]
+public class ImageGenerationController : BaseController
+{
+ public ImageGenerationController(IConfiguration configuration, ILoggerFactory loggerFactory, IWebHostEnvironment environment) : base(configuration, loggerFactory, environment)
+ { }
+
+ [HttpGet]
+ [EndpointMetrics("image.generate.get.templates")]
+ public Task