Polish UI, harden company creation, and add error pages
This commit is contained in:
@@ -57,6 +57,7 @@ public sealed class AuthController : ControllerBase
|
||||
string? LastName,
|
||||
string? DisplayName,
|
||||
string? ProfileCvText,
|
||||
string? AvatarImageDataUrl,
|
||||
IList<string> Roles,
|
||||
GoogleLinkDto? GoogleLink);
|
||||
public sealed record UpdateProfileRequest(string? Email, string? UserName, string? FirstName, string? LastName, string? DisplayName, string? ProfileCvText);
|
||||
@@ -172,6 +173,7 @@ public sealed class AuthController : ControllerBase
|
||||
LastName: User.FindFirstValue("family_name"),
|
||||
DisplayName: User.FindFirstValue("name"),
|
||||
ProfileCvText: null,
|
||||
AvatarImageDataUrl: null,
|
||||
Roles: Array.Empty<string>(),
|
||||
GoogleLink: provider == "google" ? new GoogleLinkDto(false, email, null) : null));
|
||||
}
|
||||
@@ -277,6 +279,70 @@ public sealed class AuthController : ControllerBase
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("avatar")]
|
||||
[Authorize(AuthenticationSchemes = "local")]
|
||||
[RequestSizeLimit(5_000_000)]
|
||||
public async Task<IActionResult> UploadAvatar([FromForm] IFormFile? file)
|
||||
{
|
||||
var user = await _users.GetUserAsync(User);
|
||||
if (user is null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
if (file is null || file.Length == 0)
|
||||
{
|
||||
return BadRequest("Image file is required.");
|
||||
}
|
||||
|
||||
if (!string.Equals(file.ContentType, "image/png", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(file.ContentType, "image/jpeg", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(file.ContentType, "image/webp", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return BadRequest("Only PNG, JPEG, or WebP images are supported.");
|
||||
}
|
||||
|
||||
if (file.Length > 5_000_000)
|
||||
{
|
||||
return BadRequest("Avatar image is too large.");
|
||||
}
|
||||
|
||||
await using var stream = file.OpenReadStream();
|
||||
using var memory = new MemoryStream();
|
||||
await stream.CopyToAsync(memory);
|
||||
var bytes = memory.ToArray();
|
||||
var base64 = Convert.ToBase64String(bytes);
|
||||
user.AvatarImageDataUrl = $"data:{file.ContentType};base64,{base64}";
|
||||
|
||||
var result = await _users.UpdateAsync(user);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return BadRequest(string.Join("; ", result.Errors.Select(e => e.Description)));
|
||||
}
|
||||
|
||||
return Ok(new { avatarImageDataUrl = user.AvatarImageDataUrl });
|
||||
}
|
||||
|
||||
[HttpDelete("avatar")]
|
||||
[Authorize(AuthenticationSchemes = "local")]
|
||||
public async Task<IActionResult> DeleteAvatar()
|
||||
{
|
||||
var user = await _users.GetUserAsync(User);
|
||||
if (user is null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
user.AvatarImageDataUrl = null;
|
||||
var result = await _users.UpdateAsync(user);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return BadRequest(string.Join("; ", result.Errors.Select(e => e.Description)));
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
public sealed record ChangePasswordRequest(string CurrentPassword, string NewPassword);
|
||||
|
||||
[HttpPost("change-password")]
|
||||
@@ -374,6 +440,7 @@ public sealed class AuthController : ControllerBase
|
||||
LastName: user.LastName,
|
||||
DisplayName: user.DisplayName,
|
||||
ProfileCvText: user.ProfileCvText,
|
||||
AvatarImageDataUrl: user.AvatarImageDataUrl,
|
||||
Roles: roles,
|
||||
GoogleLink: new GoogleLinkDto(
|
||||
Linked: !string.IsNullOrWhiteSpace(user.GoogleSubject),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using JobTrackerApi.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
@@ -15,301 +14,7 @@ namespace JobTrackerApi.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "7.0.17");
|
||||
|
||||
modelBuilder.Entity("JobTrackerApi.Models.Company", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Location")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Source")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Companies");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("JobTrackerApi.Models.Attachment", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("FileName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FilePath")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("UploadDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FileType")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("FileSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("JobApplicationId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobApplicationId");
|
||||
|
||||
b.ToTable("Attachments");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("JobTrackerApi.Models.Correspondence", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Date")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("From")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("JobApplicationId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobApplicationId");
|
||||
|
||||
b.ToTable("Correspondences");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("JobTrackerApi.Models.JobApplication", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("CompanyId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CoverLetterText")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("DateApplied")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("DeletedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("Deadline")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DescriptionLanguage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("FeedbackRequestedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("FollowUpAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsDeleted")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("JobTitle")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("JobUrl")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Location")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NextAction")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("ResponseDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("ResponseReceived")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Salary")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ShortSummary")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValue("Applied");
|
||||
|
||||
b.Property<string>("Tags")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TranslatedDescription")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CompanyId");
|
||||
|
||||
b.ToTable("JobApplications");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("JobTrackerApi.Models.JobEvent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("JobApplicationId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OldValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NewValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Note")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("At")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobApplicationId");
|
||||
|
||||
b.ToTable("JobEvents");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("JobTrackerApi.Models.RuleSettings", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AppliedFollowUpDays")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AppliedGhostDays")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("OfferFollowUpDays")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("OfferGhostDays")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("FeedbackFollowUpDays")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("FeedbackGhostDays")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RuleSettings");
|
||||
|
||||
b.HasData(new
|
||||
{
|
||||
Id = 1,
|
||||
AppliedFollowUpDays = 14,
|
||||
AppliedGhostDays = 30,
|
||||
OfferFollowUpDays = 7,
|
||||
OfferGhostDays = 14,
|
||||
FeedbackFollowUpDays = 7,
|
||||
FeedbackGhostDays = 14
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("JobTrackerApi.Models.Correspondence", b =>
|
||||
{
|
||||
b.HasOne("JobTrackerApi.Models.JobApplication", "JobApplication")
|
||||
.WithMany("Messages")
|
||||
.HasForeignKey("JobApplicationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("JobApplication");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("JobTrackerApi.Models.Attachment", b =>
|
||||
{
|
||||
b.HasOne("JobTrackerApi.Models.JobApplication", "JobApplication")
|
||||
.WithMany("Attachments")
|
||||
.HasForeignKey("JobApplicationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("JobApplication");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("JobTrackerApi.Models.JobApplication", b =>
|
||||
{
|
||||
b.HasOne("JobTrackerApi.Models.Company", "Company")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CompanyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Company");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("JobTrackerApi.Models.JobEvent", b =>
|
||||
{
|
||||
b.HasOne("JobTrackerApi.Models.JobApplication", "JobApplication")
|
||||
.WithMany("Events")
|
||||
.HasForeignKey("JobApplicationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("JobApplication");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("JobTrackerApi.Models.Company", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("JobTrackerApi.Models.JobApplication", b =>
|
||||
{
|
||||
b.Navigation("Attachments");
|
||||
|
||||
b.Navigation("Events");
|
||||
|
||||
b.Navigation("Messages");
|
||||
});
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.2");
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,6 +347,7 @@ CREATE TABLE IF NOT EXISTS `AspNetUsers` (
|
||||
`LastName` longtext NULL,
|
||||
`DisplayName` longtext NULL,
|
||||
`ProfileCvText` longtext NULL,
|
||||
`AvatarImageDataUrl` longtext NULL,
|
||||
`GoogleSubject` longtext NULL,
|
||||
`GoogleEmail` longtext NULL,
|
||||
`GoogleLinkedAt` datetime(6) NULL,
|
||||
@@ -498,6 +499,8 @@ CREATE TABLE IF NOT EXISTS "AspNetUsers" (
|
||||
"FirstName" TEXT NULL,
|
||||
"LastName" TEXT NULL,
|
||||
"DisplayName" TEXT NULL,
|
||||
"ProfileCvText" TEXT NULL,
|
||||
"AvatarImageDataUrl" TEXT NULL,
|
||||
"GoogleSubject" TEXT NULL,
|
||||
"GoogleEmail" TEXT NULL,
|
||||
"GoogleLinkedAt" TEXT NULL
|
||||
@@ -570,6 +573,7 @@ CREATE TABLE IF NOT EXISTS "AspNetUserTokens" (
|
||||
EnsureColumn(conn, "AspNetUsers", "LastName", "ALTER TABLE AspNetUsers ADD COLUMN LastName TEXT NULL;");
|
||||
EnsureColumn(conn, "AspNetUsers", "DisplayName", "ALTER TABLE AspNetUsers ADD COLUMN DisplayName TEXT NULL;");
|
||||
EnsureColumn(conn, "AspNetUsers", "ProfileCvText", "ALTER TABLE AspNetUsers ADD COLUMN ProfileCvText TEXT NULL;");
|
||||
EnsureColumn(conn, "AspNetUsers", "AvatarImageDataUrl", "ALTER TABLE AspNetUsers ADD COLUMN AvatarImageDataUrl TEXT NULL;");
|
||||
EnsureColumn(conn, "AspNetUsers", "GoogleSubject", "ALTER TABLE AspNetUsers ADD COLUMN GoogleSubject TEXT NULL;");
|
||||
EnsureColumn(conn, "AspNetUsers", "GoogleEmail", "ALTER TABLE AspNetUsers ADD COLUMN GoogleEmail TEXT NULL;");
|
||||
EnsureColumn(conn, "AspNetUsers", "GoogleLinkedAt", "ALTER TABLE AspNetUsers ADD COLUMN GoogleLinkedAt TEXT NULL;");
|
||||
@@ -680,6 +684,61 @@ CREATE TABLE IF NOT EXISTS "GmailConnections" (
|
||||
using var conn = new MySqlConnection(cs);
|
||||
conn.Open();
|
||||
EnsureIdentityTablesMySql(conn);
|
||||
|
||||
static bool MySqlColumnExists(MySqlConnection c, string table, string column)
|
||||
{
|
||||
using var cmd = c.CreateCommand();
|
||||
cmd.CommandText = "SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND COLUMN_NAME = @column LIMIT 1;";
|
||||
|
||||
cmd.Parameters.AddWithValue("@schema", c.Database);
|
||||
cmd.Parameters.AddWithValue("@table", table);
|
||||
cmd.Parameters.AddWithValue("@column", column);
|
||||
|
||||
return cmd.ExecuteScalar() is not null;
|
||||
}
|
||||
|
||||
static bool MySqlIndexExists(MySqlConnection c, string table, string indexName)
|
||||
{
|
||||
using var cmd = c.CreateCommand();
|
||||
cmd.CommandText = "SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND INDEX_NAME = @index LIMIT 1;";
|
||||
|
||||
cmd.Parameters.AddWithValue("@schema", c.Database);
|
||||
cmd.Parameters.AddWithValue("@table", table);
|
||||
cmd.Parameters.AddWithValue("@index", indexName);
|
||||
|
||||
return cmd.ExecuteScalar() is not null;
|
||||
}
|
||||
|
||||
static void EnsureMySqlColumn(MySqlConnection c, string table, string column, string ddl)
|
||||
{
|
||||
using var existsCmd = c.CreateCommand();
|
||||
existsCmd.CommandText = "SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table LIMIT 1;";
|
||||
existsCmd.Parameters.AddWithValue("@schema", c.Database);
|
||||
existsCmd.Parameters.AddWithValue("@table", table);
|
||||
if (existsCmd.ExecuteScalar() is null) return;
|
||||
|
||||
if (MySqlColumnExists(c, table, column)) return;
|
||||
using var ddlCmd = c.CreateCommand();
|
||||
ddlCmd.CommandText = ddl;
|
||||
ddlCmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
EnsureMySqlColumn(conn, "Companies", "OwnerUserId", "ALTER TABLE `Companies` ADD COLUMN `OwnerUserId` varchar(255) NULL;");
|
||||
EnsureMySqlColumn(conn, "JobApplications", "OwnerUserId", "ALTER TABLE `JobApplications` ADD COLUMN `OwnerUserId` varchar(255) NULL;");
|
||||
|
||||
if (!MySqlIndexExists(conn, "Companies", "IX_Companies_OwnerUserId"))
|
||||
{
|
||||
using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "CREATE INDEX `IX_Companies_OwnerUserId` ON `Companies` (`OwnerUserId`);";
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
if (!MySqlIndexExists(conn, "JobApplications", "IX_JobApplications_OwnerUserId"))
|
||||
{
|
||||
using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "CREATE INDEX `IX_JobApplications_OwnerUserId` ON `JobApplications` (`OwnerUserId`);";
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"Cors": {
|
||||
"Origins": [
|
||||
"http://localhost:3000",
|
||||
"https://jobs.cesnimda.uk"
|
||||
]
|
||||
},
|
||||
"Exports": {
|
||||
"DailyEnabled": true,
|
||||
"DailyFolder": "exports",
|
||||
"DailyHourLocal": 2
|
||||
},
|
||||
"Auth": {
|
||||
"Require": true,
|
||||
"AllowRegistration": false,
|
||||
"JwtKey": "Y00VuqZehhsMiNa8elch7q7FOlPm5ncugKJtMOpFn3P2xNtrZVfvGxVP2bKbnzL6rI08/H6vZGNBYh1dHh71/g==",
|
||||
"JwtIssuer": "JobTrackerApi",
|
||||
"JwtAudience": "job-tracker-ui",
|
||||
"JwtExpiresMinutes": 720,
|
||||
"AdminEmail": "dj@cesnimda.co.uk",
|
||||
"AdminPassword": "Leethacks12",
|
||||
"GoogleClientId": "723556162227-llqucvpog2esn1dutmtvuul1lv374or6.apps.googleusercontent.com"
|
||||
},
|
||||
"App": {
|
||||
"PublicBaseUrl": "https://jobs.cesnimda.uk"
|
||||
},
|
||||
"Email": {
|
||||
"Enabled": false,
|
||||
"SmtpHost": "smtp.gmail.com",
|
||||
"SmtpPort": 587,
|
||||
"SmtpUser": "CHANGE_ME_GMAIL_ADDRESS",
|
||||
"SmtpPassword": "CHANGE_ME_GOOGLE_APP_PASSWORD",
|
||||
"From": "CHANGE_ME_GMAIL_ADDRESS",
|
||||
"FromName": "Job Tracker",
|
||||
"SmtpEnableSsl": true,
|
||||
"SmtpTimeoutMs": 15000
|
||||
}
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"Cors": {
|
||||
"Origins": [
|
||||
"http://localhost:3000",
|
||||
"https://jobs.cesnimda.uk"
|
||||
]
|
||||
},
|
||||
"Exports": {
|
||||
"DailyEnabled": true,
|
||||
"DailyFolder": "exports",
|
||||
"DailyHourLocal": 2
|
||||
},
|
||||
"Auth": {
|
||||
"Require": true,
|
||||
"AllowRegistration": false,
|
||||
"JwtKey": "CHANGE_ME_DEV_ONLY_LONG_RANDOM_SECRET",
|
||||
"JwtIssuer": "JobTrackerApi",
|
||||
"JwtAudience": "job-tracker-ui",
|
||||
"JwtExpiresMinutes": 720,
|
||||
"AdminEmail": "admin@example.com",
|
||||
"AdminPassword": "CHANGE_ME_STRONG_DEV_PASSWORD",
|
||||
"GoogleClientId": "CHANGE_ME_GOOGLE_CLIENT_ID"
|
||||
},
|
||||
"App": {
|
||||
"PublicBaseUrl": "https://jobs.cesnimda.uk"
|
||||
},
|
||||
"Email": {
|
||||
"Enabled": false,
|
||||
"SmtpHost": "smtp.gmail.com",
|
||||
"SmtpPort": 587,
|
||||
"SmtpUser": "CHANGE_ME_GMAIL_ADDRESS",
|
||||
"SmtpPassword": "CHANGE_ME_GOOGLE_APP_PASSWORD",
|
||||
"From": "CHANGE_ME_GMAIL_ADDRESS",
|
||||
"FromName": "Jobbjakt",
|
||||
"SmtpEnableSsl": true,
|
||||
"SmtpTimeoutMs": 15000
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user