public async static Task <(EDGameState gameState, UserJournal lastJournal)> LoadGameState(MSSQLDB db, Guid userIdentifier, List <UserJournal> userJournals, string integrationKey, PerformContext context) { EDGameState previousGameState = null; UserJournal lastJournal = null; var firstAvailableGameState = userJournals.FirstOrDefault(); if (firstAvailableGameState != null) { lastJournal = await db.ExecuteSingleRowAsync <UserJournal>( "SELECT TOP 1 * FROM user_journal WHERE user_identifier = @user_identifier AND journal_id <= @journal_id AND last_processed_line_number > 0 AND integration_data IS NOT NULL ORDER BY journal_date DESC", new SqlParameter("user_identifier", userIdentifier), new SqlParameter("journal_id", firstAvailableGameState.JournalId) ); if (lastJournal != null && lastJournal.IntegrationData.ContainsKey(integrationKey)) { previousGameState = lastJournal.IntegrationData[integrationKey].CurrentGameState; context.WriteLine($"Found previous gamestate: {JsonSerializer.Serialize(previousGameState, new JsonSerializerOptions { WriteIndented = true })}"); } } return(previousGameState, lastJournal); }
public static async Task <IndexStatsModel> GetIndexStatsAsync(MSSQLDB _db) { return(await _db.ExecuteSingleRowAsync <IndexStatsModel>(@"SELECT COUNT_BIG(DISTINCT up.user_identifier) user_count, COUNT_BIG(journal_id) journal_count, SUM(last_processed_line_number) total_number_of_lines FROM user_profile up LEFT JOIN user_journal uj ON up.user_identifier = uj.user_identifier AND uj.last_processed_line_number > 0 WHERE up.deleted = 0")); }
public async Task OnGetAsync() { var profile = await _db.ExecuteSingleRowAsync <Profile>("SELECT * FROM user_profile WHERE user_identifier = @user_identifier", new SqlParameter("user_identifier", User.Identity.Name)); NotificationEmail = profile.NotificationEmail; EDDNEnabled = profile.SendToEDDN; if (profile.IntegrationSettings.ContainsKey("EDSM")) { var edsmSettings = profile.IntegrationSettings["EDSM"].GetTypedObject <EDSMIntegrationSettings>(); EDSM = edsmSettings; } if (profile.IntegrationSettings.ContainsKey("Canonn R&D")) { var canonnRDSettings = profile.IntegrationSettings["Canonn R&D"].GetTypedObject <CanonnRDIntegrationSettings>(); CanonnRD = canonnRDSettings; } }
public async Task <IActionResult> OnGetAsync(long journal_id) { var journalItem = await _db.ExecuteSingleRowAsync <UserJournal>( "select * from user_journal where journal_id = @journal_id AND last_processed_line_number > 0 AND user_identifier = @user_identifier", new SqlParameter("user_identifier", User.Identity.Name), new SqlParameter("journal_id", journal_id) ); if (journalItem == null) { return(NotFound()); } JournalItem = journalItem; JournalContent = await GetJournalContent(journalItem); return(Page()); }
public async Task <IActionResult> Authenticate() { var code = Request.Query["code"]; var state = Request.Query["state"]; if (!_memoryCache.TryGetValue("frontierLogin-" + HttpContext.Connection.RemoteIpAddress.ToString(), out string storedState)) { return(BadRequest("Could not find login token, try again")); } if (state != storedState) { return(Unauthorized("Invalid state, please relogin")); } var redirectUrl = string.Format("{0}://{1}{2}", Request.Scheme, Request.Host, Url.Content("~/api/journal/authenticate")); using var c = new HttpClient(); var formData = new Dictionary <string, string>(); formData.Add("grant_type", "authorization_code"); formData.Add("code", code); formData.Add("client_id", _configuration["EliteDangerous:ClientId"]); formData.Add("client_secret", _configuration["EliteDangerous:ClientSecret"]); formData.Add("state", state); formData.Add("redirect_uri", redirectUrl); var result = await c.PostAsync("https://auth.frontierstore.net/token", new FormUrlEncodedContent(formData)); var tokenInfo = JsonSerializer.Deserialize <OAuth2Response>(await result.Content.ReadAsStringAsync()); if (result.IsSuccessStatusCode) { c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokenInfo.AccessToken); result = await c.GetAsync("https://auth.frontierstore.net/me"); var profile = JsonSerializer.Deserialize <FrontierProfile>(await result.Content.ReadAsStringAsync()); var settings = new Settings { AuthToken = tokenInfo.AccessToken, TokenExpiration = DateTimeOffset.UtcNow.AddSeconds(tokenInfo.ExpiresIn), RefreshToken = tokenInfo.RefreshToken, FrontierProfile = profile }; // Move this so a service later var matchingUser = (await _db.ExecuteListAsync <Profile>(@" SELECT * FROM user_profile WHERE JSON_VALUE(user_settings, '$.FrontierProfile.customer_id') = @customerId", new SqlParameter("customerId", profile.CustomerId)) ).FirstOrDefault(); if (matchingUser != null) { // Update user with new token info await _db.ExecuteNonQueryAsync("UPDATE user_profile SET user_settings = @settings, last_notification_mail = NULL, skip_download = 0 WHERE user_identifier = @userIdentifier", new SqlParameter("settings", JsonSerializer.Serialize(settings)), new SqlParameter("userIdentifier", matchingUser.UserIdentifier) ); matchingUser.UserSettings = settings; } else { // Create new user matchingUser = await _db.ExecuteSingleRowAsync <Profile>("INSERT INTO user_profile (user_settings) OUTPUT INSERTED.* VALUES (@settings)", new SqlParameter("settings", JsonSerializer.Serialize(settings)) ); var userCount = await _db.ExecuteScalarAsync <long>("SELECT COUNT_BIG(user_identifier) FROM user_profile WHERE deleted = 0"); await SSEActivitySender.SendGlobalActivityAsync("A new user has registered!", $"We now have {userCount:N0} users registered!"); await SSEActivitySender.SendStatsActivityAsync(_db); BackgroundJob.Enqueue(() => JournalDownloader.DownloadJournalAsync(matchingUser.UserIdentifier, null)); } var claims = new List <Claim>() { new Claim(ClaimTypes.Name, matchingUser.UserIdentifier.ToString()) }; var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); var authProperties = new AuthenticationProperties { AllowRefresh = true, IsPersistent = false, IssuedUtc = DateTimeOffset.UtcNow }; await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties); return(LocalRedirect("~/Index")); } else { return(new JsonResult(await result.Content.ReadAsStringAsync())); } }
public async Task <IActionResult> OnGet() { var mod = await SharedSettings.GetIndexStatsAsync(_db); TotalUserJournalCount = mod.TotalUserJournalCount; TotalUserJournalLines = mod.TotalUserJournalLines; UserCount = mod.TotalUserCount; if (User.Identity.IsAuthenticated) { LoggedInUser = await _db.ExecuteSingleRowAsync <Profile>("SELECT * FROM user_profile WHERE user_identifier = @user_identifier", new SqlParameter("user_identifier", User.Identity.Name)); var rdb = SharedSettings.RedisClient.GetDatabase(0); var cmdrNameCache = await rdb.StringGetAsync($"commanderName:{User.Identity.Name}"); if (cmdrNameCache.IsNull) { var hc = _httpClientFactory.CreateClient(); hc.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", LoggedInUser.UserSettings.AuthToken); hc.BaseAddress = new Uri("https://companion.orerve.net"); var cmdrProfile = await GetProfileAsync(hc); var cmdrJson = await cmdrProfile.Content.ReadAsStringAsync(); if (!cmdrProfile.IsSuccessStatusCode || cmdrJson.Trim() == "{}") { return(Redirect("~/api/journal/logout")); } if (!string.IsNullOrWhiteSpace(cmdrJson)) { var cmdrInfo = JsonSerializer.Deserialize <EliteProfile>(cmdrJson); CommanderName = cmdrInfo.Commander.Name; if (string.IsNullOrWhiteSpace(CommanderName)) { return(Redirect("~/api/journal/logout"));; } await rdb.StringSetAsyncWithRetries($"commanderName:{User.Identity.Name}", CommanderName, TimeSpan.FromMinutes(30)); } else { CommanderName = "(Unknown, API Issues)"; } } else { CommanderName = cmdrNameCache; } LoggedInUserJournalCount = await _db.ExecuteScalarAsync <long>( "SELECT COUNT_BIG(journal_id) FROM user_journal WHERE user_identifier = @user_identifier AND last_processed_line_number > 0", new SqlParameter("user_identifier", User.Identity.Name) ); IntegrationsEnabled.Add("EDDN", LoggedInUser.SendToEDDN); foreach (var inte in LoggedInUser.IntegrationSettings) { IntegrationsEnabled.Add(inte.Key, inte.Value.GetProperty("enabled").GetBoolean()); } } return(Page()); }
public async Task <EDSystemData> GetSystemDataAsync(long systemAddress) { return(await _db.ExecuteSingleRowAsync <EDSystemData>("SELECT * FROM EliteSystem WHERE SystemAddress = @systemAddress", new SqlParameter("systemAddress", systemAddress))); }
public static async Task DownloadJournalAsync(Guid userIdentifier, PerformContext context) { using (var rlock = new RedisJobLock($"JournalDownloader.DownloadJournal.{userIdentifier}")) { if (!rlock.TryTakeLock()) { return; } context.WriteLine($"Looking for journals for user {userIdentifier}"); using (var scope = Startup.ServiceProvider.CreateScope()) { MSSQLDB db = scope.ServiceProvider.GetRequiredService <MSSQLDB>(); IConfiguration configuration = scope.ServiceProvider.GetRequiredService <IConfiguration>(); MinioClient minioClient = scope.ServiceProvider.GetRequiredService <MinioClient>(); var discordClient = scope.ServiceProvider.GetRequiredService <DiscordWebhook>(); var user = await db.ExecuteSingleRowAsync <Profile>( @"SELECT * FROM user_profile WHERE user_identifier = @user_identifier AND last_notification_mail IS NULL AND deleted = 0 AND skip_download = 0", new SqlParameter("user_identifier", userIdentifier) ); if (user == null) { return; } var authToken = user.UserSettings.AuthToken; var hc = SharedSettings.GetHttpClient(scope); hc.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authToken); hc.BaseAddress = new Uri("https://companion.orerve.net"); var profile = await GetProfileAsync(hc); var profileJson = await profile.Content.ReadAsStringAsync(); if (!profile.IsSuccessStatusCode || string.IsNullOrWhiteSpace(profileJson)) { context.WriteLine($"Invalid statuscode: {profile.StatusCode} {profile.ReasonPhrase}"); bool resetAuth = false; switch (profile.StatusCode) { case HttpStatusCode.BadRequest: // User does not potentially own the game resetAuth = true; break; case HttpStatusCode.Unauthorized: // Invalid token (or Epic games) resetAuth = true; break; } if (string.IsNullOrWhiteSpace(profileJson)) { resetAuth = true; } if (resetAuth) { await SSEActivitySender.SendUserActivityAsync(user.UserIdentifier, "Could not authorize you", "Sorry, but there seems to be something wrong with your account. Please contact us so we can try and figure out what's wrong!" ); context.WriteLine("Bailing out early, user doesn't own Elite or has issues with cAPI auth"); if (!string.IsNullOrWhiteSpace(user.NotificationEmail)) { context.WriteLine("User cannot be fetched, asking them to reauthenticate"); await SendLoginNotificationMethod.SendLoginNotification(db, configuration, user); } } return; } var profileData = JsonSerializer.Deserialize <EliteProfile>(profileJson); context.WriteLine(profileJson); if (profileJson == "{}") { if (!string.IsNullOrWhiteSpace(user.NotificationEmail)) { context.WriteLine("User cannot be fetched, asking them to reauthenticate"); await SendLoginNotificationMethod.SendLoginNotification(db, configuration, user); } return; } context.WriteLine($"Downloading journals for {profileData.Commander.Name}"); DateTime journalDate = DateTime.Today.AddDays(-25); await SSEActivitySender.SendUserActivityAsync(user.UserIdentifier, "Downloading your journals", "We're beginning to download your journals now, a few notifications may pop up." ); while (journalDate.Date != DateTime.Today) { context.WriteLine($"Fetching data for {journalDate.ToString("yyyy-MM-dd")}"); var req = await TryGetJournalAsync(discordClient, journalDate, user, db, hc, minioClient, context); if (req.shouldBail) { // Failed to get loop journal context.WriteLine($"Bailing because of errors"); return; } journalDate = journalDate.AddDays(1); } context.WriteLine($"Fetching data for {journalDate.ToString("yyyy-MM-dd")}"); var reqOut = await TryGetJournalAsync(discordClient, journalDate, user, db, hc, minioClient, context); if (reqOut.shouldBail) { // Failed to get loop journal context.WriteLine($"Bailing because of errors"); return; } if (user.SendToEDDN && !RedisJobLock.IsLocked($"EDDNUserUploader.UploadAsync.{user.UserIdentifier}")) { var userJournals = await db.ExecuteScalarAsync <long>( "SELECT COUNT_BIG(journal_id) FROM user_journal WHERE user_identifier = @user_identifier AND sent_to_eddn = 0 AND last_processed_line_number >= sent_to_eddn_line", new SqlParameter("user_identifier", userIdentifier) ); if (userJournals > 0) { context.WriteLine($"Sending {userJournals} journals to EDDN"); BackgroundJob.Enqueue(() => EDDNUserUploader.UploadAsync(user.UserIdentifier, profileData.Commander.Name, null)); } } if (user.IntegrationSettings.ContainsKey("EDSM") && user.IntegrationSettings["EDSM"].GetTypedObject <EDSMIntegrationSettings>().Enabled) { var userJournals = await db.ExecuteScalarAsync <long>( "SELECT COUNT_BIG(journal_id) FROM user_journal WHERE user_identifier = @user_identifier AND ISNULL(JSON_VALUE(integration_data, '$.EDSM.lastSentLineNumber'), '0') < last_processed_line_number AND last_processed_line_number > 0 AND ISNULL(JSON_VALUE(integration_data, '$.EDSM.fullySent'), 'false') = 'false'", new SqlParameter("user_identifier", userIdentifier) ); if (userJournals > 0) { if (!RedisJobLock.IsLocked($"EDSMUserUploader.UploadAsync.{user.UserIdentifier}")) { context.WriteLine($"Sending {userJournals} journals to EDSM"); BackgroundJob.Enqueue(() => EDSMUserUploader.UploadAsync(user.UserIdentifier, null)); } } } if (user.IntegrationSettings.ContainsKey("Canonn R&D") && user.IntegrationSettings["Canonn R&D"].GetTypedObject <CanonnRDIntegrationSettings>().Enabled) { var userJournals = await db.ExecuteScalarAsync <long>( "SELECT COUNT_BIG(journal_id) FROM user_journal WHERE user_identifier = @user_identifier AND ISNULL(JSON_VALUE(integration_data, '$.\"Canonn R\\u0026D\".lastSentLineNumber'), '0') <= last_processed_line_number AND last_processed_line_number > 0 AND ISNULL(JSON_VALUE(integration_data, '$.\"Canonn R\\u0026D\".fullySent'), 'false') = 'false'", new SqlParameter("user_identifier", userIdentifier) ); if (userJournals > 0) { if (!RedisJobLock.IsLocked($"CanonnRDUserUploader.UploadAsync.{user.UserIdentifier}")) { context.WriteLine($"Sending {userJournals} journals to Canonn"); BackgroundJob.Enqueue(() => CanonnRDUserUploader.UploadAsync(user.UserIdentifier, profileData.Commander.Name, null)); } } } context.WriteLine("All done!"); } } }