예제 #1
0
        public static async Task RefreshUserTokensAsync(PerformContext context)
        {
            context.WriteLine("Looking for tokens to refresh!");
            using (var scope = Startup.ServiceProvider.CreateScope())
            {
                MSSQLDB db = scope.ServiceProvider.GetRequiredService <MSSQLDB>();

                IConfiguration configuration = scope.ServiceProvider.GetRequiredService <IConfiguration>();

                var soonExpiringUsers = await db.ExecuteListAsync <Shared.Models.User.Profile>(
                    @"SELECT *
FROM user_profile
WHERE DATEDIFF(MINUTE, GETUTCDATE(), CAST(JSON_VALUE(user_settings, '$.TokenExpiration') as DATETIMEOFFSET)) < 10
AND last_notification_mail IS NULL
AND deleted = 0"
                    );

                context.WriteLine($"Found {soonExpiringUsers.Count} user(s) to refresh tokens for");

                IHttpClientFactory _hcf = scope.ServiceProvider.GetRequiredService <IHttpClientFactory>();

                var hc = _hcf.CreateClient();

                foreach (var user in soonExpiringUsers.WithProgress(context))
                {
                    var res = await hc.PostAsync("https://auth.frontierstore.net/token",
                                                 new FormUrlEncodedContent(new Dictionary <string, string>
                    {
                        { "grant_type", "refresh_token" },
                        { "refresh_token", user.UserSettings.RefreshToken.ToString() },
                        { "client_id", configuration["EliteDangerous:ClientId"] }
                    })
                                                 );

                    if (!res.IsSuccessStatusCode)
                    {
                        // The user is not authorized to perform more automatic refreshes of the token
                        // Send notification to the user that they need to re-login if they want to keep getting their journals stored

                        if (!string.IsNullOrWhiteSpace(user.NotificationEmail))
                        {
                            await SendLoginNotificationMethod.SendLoginNotification(db, configuration, user);
                        }
                    }
                    else
                    {
                        // We managed to grab a new token, lets save it!

                        var tokenInfo = JsonSerializer.Deserialize <OAuth2Response>(await res.Content.ReadAsStringAsync());

                        var settings = new Settings
                        {
                            AuthToken       = tokenInfo.AccessToken,
                            TokenExpiration = DateTimeOffset.UtcNow.AddSeconds(tokenInfo.ExpiresIn),
                            RefreshToken    = tokenInfo.RefreshToken,
                            FrontierProfile = user.UserSettings.FrontierProfile
                        };

                        // Update user with new token info
                        await db.ExecuteNonQueryAsync("UPDATE user_profile SET user_settings = @settings, last_notification_mail = NULL WHERE user_identifier = @userIdentifier",
                                                      new SqlParameter("@settings", JsonSerializer.Serialize(settings)),
                                                      new SqlParameter("@userIdentifier", user.UserIdentifier)
                                                      );
                    }
                }
            }
        }
예제 #2
0
        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!");
                }
            }
        }