Beispiel #1
0
        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"));
        }
Beispiel #3
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());
        }
Beispiel #5
0
        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());
        }
Beispiel #7
0
 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!");
                }
            }
        }