| using ProtoBuf; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace GrpcShared.DTO.Db | |||||
| { | |||||
| [ProtoContract] | |||||
| public class DbRequestMessage | |||||
| { | |||||
| [ProtoMember(1)] | |||||
| public string Id { get; set; } | |||||
| } | |||||
| } |
| using ProtoBuf; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace GrpcShared.DTO.Db | |||||
| { | |||||
| [ProtoContract] | |||||
| public class SaveTrackRequest : TrackResponse | |||||
| { | |||||
| [ProtoMember(1)] | |||||
| public string UserId { get; set; } | |||||
| } | |||||
| } |
| using ProtoBuf; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace GrpcShared.DTO.Db | |||||
| { | |||||
| [ProtoContract] | |||||
| [ProtoInclude(6, typeof(SaveTrackRequest))] | |||||
| public class TrackResponse | |||||
| { | |||||
| [ProtoMember(1)] | |||||
| public string Id { get; set; } | |||||
| [ProtoMember(2)] | |||||
| public string Title { get; set; } | |||||
| [ProtoMember(3)] | |||||
| public string Artist { get; set; } | |||||
| [ProtoMember(4)] | |||||
| public string Album { get; set; } | |||||
| [ProtoMember(5)] | |||||
| public string TrackId { get; set; } | |||||
| } | |||||
| } |
| using ProtoBuf; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace GrpcShared.DTO.Db | |||||
| { | |||||
| [ProtoContract] | |||||
| public class UserResponse | |||||
| { | |||||
| [ProtoMember(1)] | |||||
| public string Id { get; set; } | |||||
| [ProtoMember(2)] | |||||
| public string Token { get; set; } | |||||
| [ProtoMember(3)] | |||||
| public string RefreshToken{ get; set; } | |||||
| } | |||||
| } |
| using ProtoBuf; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace GrpcShared.DTO | |||||
| { | |||||
| [ProtoContract] | |||||
| public class VoidMessage | |||||
| { | |||||
| [ProtoMember(1)] | |||||
| public string InsertedId { get; set; } | |||||
| } | |||||
| } |
| using GrpcShared.DTO; | |||||
| using GrpcShared.DTO.Db; | |||||
| using ProtoBuf.Grpc.Configuration; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace GrpcShared.Interfaces | |||||
| { | |||||
| [Service] | |||||
| public interface IIdentityService | |||||
| { | |||||
| Task<UserResponse> GetTokenByIdAsync(DbRequestMessage id); | |||||
| Task<List<UserResponse>> ListUsersAsync(VoidMessage msg); | |||||
| Task<TrackResponse> GetTrackByUserAsync(DbRequestMessage id); | |||||
| Task<VoidMessage> SaveTrackAsync(SaveTrackRequest track); | |||||
| Task<VoidMessage> DeleteTrackAsync(DbRequestMessage id); | |||||
| Task<VoidMessage> SaveUserAsync(UserResponse user); | |||||
| Task<VoidMessage> DeleteUserAsync(DbRequestMessage user); | |||||
| Task<VoidMessage> UpdateTokenAsync (UserResponse user); | |||||
| } | |||||
| } |
| <PackageReference Include="Grpc.AspNetCore" Version="2.40.0" /> | <PackageReference Include="Grpc.AspNetCore" Version="2.40.0" /> | ||||
| <PackageReference Include="Grpc.AspNetCore.Web" Version="2.47.0" /> | <PackageReference Include="Grpc.AspNetCore.Web" Version="2.47.0" /> | ||||
| <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.8" /> | <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.8" /> | ||||
| <PackageReference Include="MongoDB.Driver" Version="2.17.1" /> | |||||
| <PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> | <PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> | ||||
| <PackageReference Include="protobuf-net.Grpc" Version="1.0.171" /> | <PackageReference Include="protobuf-net.Grpc" Version="1.0.171" /> | ||||
| <PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" /> | <PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" /> | ||||
| <ProjectReference Include="..\NemAnCore\NemAnBlazor.csproj" /> | <ProjectReference Include="..\NemAnCore\NemAnBlazor.csproj" /> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | |||||
| <Folder Include="Services\" /> | |||||
| </ItemGroup> | |||||
| </Project> | </Project> |
| namespace IdentityProvider.Models | |||||
| { | |||||
| public class SpotifyDbConfig | |||||
| { | |||||
| public string ConnectionString { get; set; } = null!; | |||||
| public string DatabaseName { get; set; } = null!; | |||||
| public string UserCollection { get; set; } = null!; | |||||
| public string TracksCollection { get; set; } = null!; | |||||
| } | |||||
| } |
| using MongoDB.Bson; | |||||
| using MongoDB.Bson.Serialization.Attributes; | |||||
| namespace IdentityProvider.Models | |||||
| { | |||||
| public class TrackModel | |||||
| { | |||||
| [BsonId] | |||||
| [BsonRepresentation(BsonType.ObjectId)] | |||||
| public string Id { get; set; } | |||||
| public string Title { get; set; } | |||||
| public string Artist { get; set; } | |||||
| public string Album { get; set; } | |||||
| public string UserId { get; set; } | |||||
| public string TrackId { get; set; } | |||||
| } | |||||
| } |
| using MongoDB.Bson; | |||||
| using MongoDB.Bson.Serialization.Attributes; | |||||
| namespace IdentityProvider.Models | |||||
| { | |||||
| public class UserModel | |||||
| { | |||||
| [BsonId] | |||||
| [BsonRepresentation(BsonType.ObjectId)] | |||||
| public string Id { get; set; } | |||||
| public string Token { get; set; } | |||||
| public string RefreshToken { get; set; } | |||||
| } | |||||
| } |
| using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
| using GrpcShared.DTO.Auth; | using GrpcShared.DTO.Auth; | ||||
| using Blazored.LocalStorage; | using Blazored.LocalStorage; | ||||
| using IdentityProvider.Models; | |||||
| using IdentityProvider.Services; | |||||
| var builder = WebApplication.CreateBuilder(args); | var builder = WebApplication.CreateBuilder(args); | ||||
| #if DEBUG | #if DEBUG | ||||
| }); | }); | ||||
| builder.Services.AddOptions(); | builder.Services.AddOptions(); | ||||
| builder.Services.AddSingleton<SpotifyDbConfig>(); | |||||
| // Additional configuration is required to successfully run gRPC on macOS. | // Additional configuration is required to successfully run gRPC on macOS. | ||||
| // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682 | // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682 | ||||
| builder.Services.AddControllersWithViews(); | builder.Services.AddControllersWithViews(); | ||||
| builder.Services.AddBlazoredLocalStorage(); | builder.Services.AddBlazoredLocalStorage(); | ||||
| //call spotify api | //call spotify api | ||||
| builder.Services.AddHttpClient(); | builder.Services.AddHttpClient(); | ||||
| builder.Services.Configure<CodeRequest>(builder.Configuration.GetSection("AuthParams")); | |||||
| builder.Services.Configure<CodeRequest>( | |||||
| builder.Configuration.GetSection("AuthParams")); | |||||
| builder.Services.Configure<SpotifyDbConfig>( | |||||
| builder.Configuration.GetSection("SpotifyDb")); | |||||
| var app = builder.Build(); | var app = builder.Build(); | ||||
| app.MapGrpcService<AuthService>().EnableGrpcWeb(); | app.MapGrpcService<AuthService>().EnableGrpcWeb(); | ||||
| app.MapGrpcService<TrackService>().EnableGrpcWeb(); | app.MapGrpcService<TrackService>().EnableGrpcWeb(); | ||||
| app.MapGrpcService<StatsService>().EnableGrpcWeb(); | app.MapGrpcService<StatsService>().EnableGrpcWeb(); | ||||
| app.MapGrpcService<IdentityService>().EnableGrpcWeb(); | |||||
| app.MapCodeFirstGrpcReflectionService(); | app.MapCodeFirstGrpcReflectionService(); | ||||
| { | |||||
| { | |||||
| "iisSettings": { | "iisSettings": { | ||||
| "windowsAuthentication": false, | "windowsAuthentication": false, | ||||
| "anonymousAuthentication": true, | "anonymousAuthentication": true, | ||||
| "profiles": { | "profiles": { | ||||
| "IIS Express": { | "IIS Express": { | ||||
| "commandName": "IISExpress", | "commandName": "IISExpress", | ||||
| "launchBrowser": true, | |||||
| "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", | |||||
| "environmentVariables": { | "environmentVariables": { | ||||
| "ASPNETCORE_ENVIRONMENT": "Development" | "ASPNETCORE_ENVIRONMENT": "Development" | ||||
| } | |||||
| }, | |||||
| "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" | |||||
| }, | }, | ||||
| "IdentityProvider": { | "IdentityProvider": { | ||||
| "commandName": "Project", | "commandName": "Project", | ||||
| "dotnetRunMessages": "true", | |||||
| "launchBrowser": true, | "launchBrowser": true, | ||||
| "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", | |||||
| "applicationUrl": "https://localhost:5001;http://localhost:5000", | |||||
| "environmentVariables": { | "environmentVariables": { | ||||
| "ASPNETCORE_ENVIRONMENT": "Development" | "ASPNETCORE_ENVIRONMENT": "Development" | ||||
| } | |||||
| }, | |||||
| "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", | |||||
| "applicationUrl": "https://localhost:5001;http://localhost:5000", | |||||
| "dotnetRunMessages": "true" | |||||
| } | } | ||||
| } | } | ||||
| } | |||||
| } |
| using GrpcShared.DTO; | |||||
| using GrpcShared.DTO.Db; | |||||
| using GrpcShared.Interfaces; | |||||
| using IdentityProvider.Models; | |||||
| using Microsoft.Extensions.Options; | |||||
| using MongoDB.Driver; | |||||
| namespace IdentityProvider.Services | |||||
| { | |||||
| public class IdentityService : IIdentityService | |||||
| { | |||||
| private readonly IMongoCollection<UserModel> _userCollection; | |||||
| private readonly IMongoCollection<TrackModel> _trackCollection; | |||||
| public IdentityService(IOptions<SpotifyDbConfig> spotifyDbConfig) | |||||
| { | |||||
| var mongoClient = new MongoClient(spotifyDbConfig.Value.ConnectionString); | |||||
| var mongoDbContext = mongoClient.GetDatabase(spotifyDbConfig.Value.DatabaseName); | |||||
| _userCollection = mongoDbContext.GetCollection<UserModel>(spotifyDbConfig.Value.UserCollection); | |||||
| _trackCollection = mongoDbContext.GetCollection<TrackModel>(spotifyDbConfig.Value.TracksCollection); | |||||
| } | |||||
| public async Task<List<UserResponse>> ListUsersAsync(VoidMessage msg) | |||||
| { | |||||
| List<UserModel> users = await _userCollection.Find(_ => true).ToListAsync(); | |||||
| //map users to match the grpc response | |||||
| var usersRes = users.Select(u => new UserResponse | |||||
| { | |||||
| Id = u.Id, | |||||
| Token = u.Token | |||||
| }).ToList(); | |||||
| return usersRes; | |||||
| } | |||||
| public async Task<UserResponse> GetTokenByIdAsync(DbRequestMessage request) | |||||
| { | |||||
| UserModel user = await _userCollection.Find(u => u.Id == request.Id).FirstOrDefaultAsync(); | |||||
| if (user != null) | |||||
| return new UserResponse | |||||
| { | |||||
| Id = user.Id, | |||||
| Token = user.Token | |||||
| }; | |||||
| else return new UserResponse(); | |||||
| } | |||||
| public async Task<TrackResponse> GetTrackByUserAsync(DbRequestMessage request) | |||||
| { | |||||
| TrackModel track = await _trackCollection.Find(t => t.UserId == request.Id).FirstOrDefaultAsync(); | |||||
| return new TrackResponse | |||||
| { | |||||
| Id = track.Id, | |||||
| Album = track.Album, | |||||
| Artist = track.Artist, | |||||
| Title = track.Title, | |||||
| TrackId = track.TrackId | |||||
| }; | |||||
| } | |||||
| public async Task<VoidMessage> SaveTrackAsync(SaveTrackRequest track) | |||||
| { | |||||
| TrackModel trackModel = new() | |||||
| { | |||||
| Id = track.Id, | |||||
| Title = track.Title, | |||||
| Album = track.Album, | |||||
| Artist = track.Artist, | |||||
| UserId = track.UserId, | |||||
| TrackId = track.TrackId | |||||
| }; | |||||
| //first check if there's already a song in the db, if yes, update the row | |||||
| bool song = await _trackCollection.Find(x => x.UserId == track.UserId).AnyAsync(); | |||||
| if (song) await _trackCollection.DeleteOneAsync(s => s.UserId == track.UserId); | |||||
| await _trackCollection.InsertOneAsync(trackModel); | |||||
| return new VoidMessage() { InsertedId = trackModel.Id }; | |||||
| } | |||||
| public async Task<VoidMessage> SaveUserAsync(UserResponse userRequest) | |||||
| { | |||||
| bool user = await _userCollection.Find(x => x.Id == userRequest.Id).AnyAsync(); | |||||
| UserModel tempUser = new() | |||||
| { | |||||
| Token = userRequest.Token, | |||||
| RefreshToken = userRequest.RefreshToken | |||||
| }; | |||||
| if (!user) await _userCollection.InsertOneAsync(tempUser); | |||||
| return new VoidMessage() { InsertedId = tempUser.Id }; | |||||
| } | |||||
| public async Task<VoidMessage> DeleteTrackAsync(DbRequestMessage request) | |||||
| { | |||||
| await _trackCollection.DeleteOneAsync(x => x.Id == request.Id); | |||||
| return new VoidMessage(); | |||||
| } | |||||
| public async Task<VoidMessage> DeleteUserAsync(DbRequestMessage userRequest) | |||||
| { | |||||
| //check if user exists | |||||
| bool user = await _userCollection.Find(x => x.Id == userRequest.Id).AnyAsync(); | |||||
| //delete track related to him | |||||
| if (user) | |||||
| { | |||||
| await _trackCollection.DeleteManyAsync(x => x.UserId == userRequest.Id); | |||||
| //delete user | |||||
| await _userCollection.DeleteOneAsync(x => x.Id == userRequest.Id); | |||||
| } | |||||
| return new VoidMessage(); | |||||
| } | |||||
| public async Task<VoidMessage> UpdateTokenAsync(UserResponse user) | |||||
| { | |||||
| UserModel dbUser = await _userCollection.Find(x => x.Id == user.Id).FirstOrDefaultAsync(); | |||||
| if (dbUser.Id != null) | |||||
| { | |||||
| dbUser.Token = user.Token; | |||||
| dbUser.RefreshToken = user.RefreshToken; | |||||
| await _userCollection.ReplaceOneAsync(x => x.Id == user.Id, dbUser); | |||||
| } | |||||
| return new VoidMessage | |||||
| { | |||||
| InsertedId = dbUser.Id | |||||
| }; | |||||
| } | |||||
| } | |||||
| } |
| "RedirectURI": "https://localhost:44342/callback", | "RedirectURI": "https://localhost:44342/callback", | ||||
| "Scope": "user-read-currently-playing user-read-email user-library-modify user-top-read user-read-private", | "Scope": "user-read-currently-playing user-read-email user-library-modify user-top-read user-read-private", | ||||
| "ClientSecret": "ea752433d0774fad87fab5c1ee788c8d" | "ClientSecret": "ea752433d0774fad87fab5c1ee788c8d" | ||||
| }, | |||||
| "SpotifyDb": { | |||||
| "ConnectionString": "mongodb://127.0.0.1:27017", | |||||
| "DatabaseName": "spotifyDb", | |||||
| "UserCollection": "Users", | |||||
| "TracksCollection": "Tracks" | |||||
| } | } | ||||
| } | } |
| @page "/callback" | @page "/callback" | ||||
| @using GrpcShared.DTO | |||||
| @using GrpcShared.DTO.Db | |||||
| @using NemAnBlazor.Services.Interfaces | @using NemAnBlazor.Services.Interfaces | ||||
| @inject NavigationManager NavigationMgr | @inject NavigationManager NavigationMgr | ||||
| @inject IAuthClientService AuthService | @inject IAuthClientService AuthService | ||||
| @inject Blazored.LocalStorage.ILocalStorageService localStorage | @inject Blazored.LocalStorage.ILocalStorageService localStorage | ||||
| @inject IIdentityClientService identityService | |||||
| <PageTitle>Redirecting...</PageTitle> | <PageTitle>Redirecting...</PageTitle> | ||||
| //if (response.access_token == null) NavigationMgr.NavigateTo("/"); | //if (response.access_token == null) NavigationMgr.NavigateTo("/"); | ||||
| //store access token in local storage | //store access token in local storage | ||||
| await localStorage.SetItemAsync("token", response.AccessToken); | |||||
| await localStorage.SetItemAsync("refresh_token", response.RefreshToken); | |||||
| //await localStorage.SetItemAsync("token", response.AccessToken); | |||||
| //await localStorage.SetItemAsync("refresh_token", response.RefreshToken); | |||||
| //UserResponse user = new(); | |||||
| //await identityService.SaveUserAsync(new GrpcShared.DTO.Db.UserResponse()); | |||||
| string userId = await localStorage.GetItemAsync<string>("user_info"); | |||||
| if(userId == null) | |||||
| { | |||||
| VoidMessage userRes = await identityService.SaveUserAsync(new GrpcShared.DTO.Db.UserResponse { RefreshToken = response.RefreshToken , Token = response.AccessToken}); | |||||
| if (userRes.InsertedId != null) | |||||
| await localStorage.SetItemAsync<string>("user_info", userRes.InsertedId); | |||||
| } | |||||
| //redirect to home | //redirect to home | ||||
| NavigationMgr.NavigateTo("/home"); | NavigationMgr.NavigateTo("/home"); | ||||
| } | } |
| @inject Blazored.LocalStorage.ILocalStorageService localStorage | @inject Blazored.LocalStorage.ILocalStorageService localStorage | ||||
| @inject ITrackClientService SearchService | @inject ITrackClientService SearchService | ||||
| @inject IAuthClientService AuthService | @inject IAuthClientService AuthService | ||||
| @inject IIdentityClientService identityService | |||||
| <PageTitle>Search</PageTitle> | <PageTitle>Search</PageTitle> | ||||
| private async Task Click() | private async Task Click() | ||||
| { | { | ||||
| var token = await localStorage.GetItemAsync<string>("token"); | |||||
| string refreshT = await localStorage.GetItemAsync<string>("refresh_token"); | |||||
| var userInfo = await localStorage.GetItemAsync<string>("user_info"); | |||||
| TokenMessage tokenM = new TokenMessage { Token = token, RefreshToken = refreshT }; | |||||
| var user = await identityService.GetTokenByIdAsync(new GrpcShared.DTO.Db.DbRequestMessage | |||||
| { | |||||
| Id = userInfo | |||||
| }); | |||||
| TokenMessage tokenM = new TokenMessage { Token = user.Token, RefreshToken = user.RefreshToken }; | |||||
| SearchRequest request = new() { Query = "aitch", Type = "track", Token = token }; | |||||
| SearchRequest request = new() { Query = "aitch", Type = "track", Token = user.Token }; | |||||
| try | try | ||||
| { | { | ||||
| if (searchResponse.ResponseMsg == System.Net.HttpStatusCode.Unauthorized) | if (searchResponse.ResponseMsg == System.Net.HttpStatusCode.Unauthorized) | ||||
| { | { | ||||
| string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, tokenM, localStorage); | |||||
| token = tempToken == null ? token : tempToken; | |||||
| string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, tokenM, user, localStorage, identityService); | |||||
| } | } | ||||
| } | } | ||||
| catch (RpcException e) | catch (RpcException e) |
| @page "/home" | @page "/home" | ||||
| @using Grpc.Core | @using Grpc.Core | ||||
| @using GrpcShared.DTO | @using GrpcShared.DTO | ||||
| @using GrpcShared.DTO.Db | |||||
| @using GrpcShared.DTO.Track | @using GrpcShared.DTO.Track | ||||
| @using NemAnBlazor.Services.Interfaces | @using NemAnBlazor.Services.Interfaces | ||||
| @using System.Net | @using System.Net | ||||
| @inject IStatsClientService spotifyService | @inject IStatsClientService spotifyService | ||||
| @inject ITrackClientService trackService | @inject ITrackClientService trackService | ||||
| @inject IAuthClientService AuthService | @inject IAuthClientService AuthService | ||||
| @inject NavigationManager NavigationMgr | |||||
| @inject IIdentityClientService identityService | |||||
| <h3>Home</h3> | <h3>Home</h3> | ||||
| @code { | @code { | ||||
| protected override async Task OnInitializedAsync() | protected override async Task OnInitializedAsync() | ||||
| { | { | ||||
| CurrentTrackResponse track; | |||||
| string tokenS = "", refreshT = ""; | |||||
| string userId = await localStorage.GetItemAsync<string>("user_info"); | |||||
| string tokenS = await localStorage.GetItemAsync<string>("token"); | |||||
| string refreshT = await localStorage.GetItemAsync<string>("refresh_token"); | |||||
| if (userId != null) | |||||
| { | |||||
| //get token from identity | |||||
| var userResponse = await identityService.GetTokenByIdAsync(new GrpcShared.DTO.Db.DbRequestMessage { Id = userId }); | |||||
| //tokenS = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN"; | |||||
| TokenMessage token = new TokenMessage { Token = tokenS, RefreshToken = refreshT }; | |||||
| if (userResponse != null) | |||||
| { | |||||
| tokenS = userResponse.Token; | |||||
| refreshT = userResponse.RefreshToken; | |||||
| } | |||||
| else { | |||||
| await localStorage.RemoveItemAsync("user_info"); | |||||
| NavigationMgr.NavigateTo("/"); | |||||
| } | |||||
| } | |||||
| //tokenS = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN"; | |||||
| TokenMessage token = new() { Token = tokenS, RefreshToken = refreshT }; | |||||
| UserResponse user = new() { Id = userId, RefreshToken = refreshT }; | |||||
| try | try | ||||
| { | { | ||||
| CurrentTrackResponse track = await spotifyService.GetCurrentlyPlayingTrack(token); | |||||
| track = await spotifyService.GetCurrentlyPlayingTrack(token); | |||||
| //if token expired, refresh it | //if token expired, refresh it | ||||
| if (track.ResponseMsg == System.Net.HttpStatusCode.Unauthorized) | if (track.ResponseMsg == System.Net.HttpStatusCode.Unauthorized) | ||||
| { | { | ||||
| string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, token, localStorage); | |||||
| string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, token, user, localStorage, identityService); | |||||
| tokenS = tempToken == null ? tokenS : tempToken; | tokenS = tempToken == null ? tokenS : tempToken; | ||||
| //if refreshed token is null, that means that refresh token was invalid, so you should redirect to login | //if refreshed token is null, that means that refresh token was invalid, so you should redirect to login | ||||
| } | } | ||||
| if (items.ResponseMsg == System.Net.HttpStatusCode.Unauthorized) | if (items.ResponseMsg == System.Net.HttpStatusCode.Unauthorized) | ||||
| { | { | ||||
| string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, token, localStorage); | |||||
| string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, token, user, localStorage, identityService); | |||||
| tokenS = tempToken == null ? tokenS : tempToken; | tokenS = tempToken == null ? tokenS : tempToken; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| await identityService.SaveTrackAsync(new GrpcShared.DTO.Db.SaveTrackRequest | |||||
| { | |||||
| TrackId = track.Item.Id, | |||||
| Title = track.Item.Name, | |||||
| Album = track.Item.Album.Name, | |||||
| Artist = track.Item.Artists[0].Name, | |||||
| UserId = userId | |||||
| }); | |||||
| //var resp = await identityService.ListUsersAsync(new VoidMessage()); | |||||
| var tokenFromDb = await identityService.GetTokenByIdAsync(new GrpcShared.DTO.Db.DbRequestMessage | |||||
| { | |||||
| Id = userId | |||||
| }); | |||||
| var trackByUser = await identityService.GetTrackByUserAsync(new GrpcShared.DTO.Db.DbRequestMessage | |||||
| { | |||||
| Id = userId | |||||
| }); | |||||
| //await identityService.DeleteTrackAsync(new GrpcShared.DTO.Db.DbRequestMessage | |||||
| // { | |||||
| // Id = | |||||
| // }); | |||||
| await identityService.SaveTrackAsync(new GrpcShared.DTO.Db.SaveTrackRequest | |||||
| { | |||||
| TrackId = track.Item.Id, | |||||
| Title = track.Item.Name, | |||||
| Album = track.Item.Album.Name, | |||||
| Artist = track.Item.Artists[0].Name, | |||||
| UserId = userId | |||||
| }); | |||||
| //find id from local storage | |||||
| //await identityService.DeleteUserAsync(new GrpcShared.DTO.Db.DbRequestMessage | |||||
| // { | |||||
| // Id = userId | |||||
| // }); | |||||
| } | } | ||||
| } | } |
| @inject IAuthClientService AuthService | @inject IAuthClientService AuthService | ||||
| @inject ITrackClientService SearchService | @inject ITrackClientService SearchService | ||||
| @using System.Security.Claims | @using System.Security.Claims | ||||
| @inject Blazored.LocalStorage.ILocalStorageService localStorage | |||||
| @code { | @code { | ||||
| private string? message; | private string? message; | ||||
| protected override async Task OnInitializedAsync() | |||||
| { | |||||
| var userId = await localStorage.GetItemAsync<string>("user_info"); | |||||
| if (userId != null) NavigationManager.NavigateTo("/home"); | |||||
| } | |||||
| private async Task LoginUser() | private async Task LoginUser() | ||||
| { | { | ||||
| //var response = await SearchService.GetListSearchAsync(new GrpcShared.DTO.Search.SearchRequest() { Query="venom", Type = "track"}); | //var response = await SearchService.GetListSearchAsync(new GrpcShared.DTO.Search.SearchRequest() { Query="venom", Type = "track"}); |
| builder.Services.AddScoped<ITrackClientService, TrackClientService>(); | builder.Services.AddScoped<ITrackClientService, TrackClientService>(); | ||||
| builder.Services.AddScoped<IAuthClientService, AuthClientService>(); | builder.Services.AddScoped<IAuthClientService, AuthClientService>(); | ||||
| builder.Services.AddScoped<IStatsClientService, StatsClientService>(); | builder.Services.AddScoped<IStatsClientService, StatsClientService>(); | ||||
| builder.Services.AddScoped<IIdentityClientService, IdentityClientService>(); | |||||
| builder.Services.AddBlazoredLocalStorage(); | builder.Services.AddBlazoredLocalStorage(); | ||||
| builder.Services.AddScoped<AuthenticationStateProvider, AuthClientService>(); | builder.Services.AddScoped<AuthenticationStateProvider, AuthClientService>(); | ||||
| builder.Services.AddAuthorizationCore(); | builder.Services.AddAuthorizationCore(); |
| using Grpc.Net.Client; | |||||
| using GrpcShared.DTO; | |||||
| using GrpcShared.DTO.Db; | |||||
| using GrpcShared.Interfaces; | |||||
| using NemAnBlazor.Services.Interfaces; | |||||
| using ProtoBuf.Grpc.Client; | |||||
| namespace NemAnBlazor.Services | |||||
| { | |||||
| public class IdentityClientService : IIdentityClientService | |||||
| { | |||||
| private IIdentityService _serviceClient; | |||||
| public IdentityClientService(GrpcChannel channel) | |||||
| { | |||||
| _serviceClient = channel.CreateGrpcService<IIdentityService>(); | |||||
| } | |||||
| public async Task<VoidMessage> DeleteTrackAsync(DbRequestMessage id) | |||||
| { | |||||
| return await _serviceClient.DeleteTrackAsync(id); | |||||
| } | |||||
| public async Task<VoidMessage> DeleteUserAsync(DbRequestMessage user) | |||||
| { | |||||
| return await _serviceClient.DeleteUserAsync(user); | |||||
| } | |||||
| public async Task<UserResponse> GetTokenByIdAsync(DbRequestMessage id) | |||||
| { | |||||
| return await _serviceClient.GetTokenByIdAsync(id); | |||||
| } | |||||
| public async Task<TrackResponse> GetTrackByUserAsync(DbRequestMessage id) | |||||
| { | |||||
| return await _serviceClient.GetTrackByUserAsync(id); | |||||
| } | |||||
| public async Task<List<UserResponse>> ListUsersAsync(VoidMessage msg) | |||||
| { | |||||
| return await _serviceClient.ListUsersAsync(msg); | |||||
| } | |||||
| public async Task<VoidMessage> SaveTrackAsync(SaveTrackRequest track) | |||||
| { | |||||
| return await _serviceClient.SaveTrackAsync(track); | |||||
| } | |||||
| public async Task<VoidMessage> SaveUserAsync(UserResponse user) | |||||
| { | |||||
| return await _serviceClient.SaveUserAsync(user); | |||||
| } | |||||
| public async Task<VoidMessage> UpdateTokenAsync(UserResponse user) | |||||
| { | |||||
| return await _serviceClient.UpdateTokenAsync(user); | |||||
| } | |||||
| } | |||||
| } |
| using GrpcShared.DTO; | |||||
| using GrpcShared.DTO.Db; | |||||
| namespace NemAnBlazor.Services.Interfaces | |||||
| { | |||||
| public interface IIdentityClientService | |||||
| { | |||||
| Task<UserResponse> GetTokenByIdAsync(DbRequestMessage id); | |||||
| Task<List<UserResponse>> ListUsersAsync(VoidMessage msg); | |||||
| Task<TrackResponse> GetTrackByUserAsync(DbRequestMessage id); | |||||
| Task<VoidMessage> SaveTrackAsync(SaveTrackRequest track); | |||||
| Task<VoidMessage> DeleteTrackAsync(DbRequestMessage id); | |||||
| Task<VoidMessage> SaveUserAsync(UserResponse user); | |||||
| Task<VoidMessage> DeleteUserAsync(DbRequestMessage user); | |||||
| Task<VoidMessage> UpdateTokenAsync(UserResponse user); | |||||
| } | |||||
| } |
| using Blazored.LocalStorage; | using Blazored.LocalStorage; | ||||
| using GrpcShared.DTO; | using GrpcShared.DTO; | ||||
| using GrpcShared.DTO.Db; | |||||
| using NemAnBlazor.Services.Interfaces; | using NemAnBlazor.Services.Interfaces; | ||||
| namespace NemAnBlazor | namespace NemAnBlazor | ||||
| { | { | ||||
| public static class SpotifyHelper | public static class SpotifyHelper | ||||
| { | { | ||||
| public static async Task<string?> TryRefreshToken(IAuthClientService authService, TokenMessage msg, ILocalStorageService localStorage) | |||||
| public static async Task<string?> TryRefreshToken | |||||
| (IAuthClientService authService, | |||||
| TokenMessage msg,UserResponse user, | |||||
| ILocalStorageService localStorage, | |||||
| IIdentityClientService identityService) | |||||
| { | { | ||||
| var refreshResponse = await authService.RefreshAccessToken(msg); | var refreshResponse = await authService.RefreshAccessToken(msg); | ||||
| if (refreshResponse.AccessToken != null) | if (refreshResponse.AccessToken != null) | ||||
| { | { | ||||
| await localStorage.SetItemAsync<string>("token", refreshResponse.AccessToken); | |||||
| await identityService.UpdateTokenAsync(new UserResponse | |||||
| { | |||||
| Id = user.Id, | |||||
| Token = refreshResponse.AccessToken, | |||||
| RefreshToken = user.RefreshToken | |||||
| }); | |||||
| return refreshResponse.AccessToken; | return refreshResponse.AccessToken; | ||||
| } | } | ||||
| else return null; | else return null; |
| <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" /> | <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" /> | ||||
| <PackageReference Include="protobuf-net.Grpc" Version="1.0.171" /> | <PackageReference Include="protobuf-net.Grpc" Version="1.0.171" /> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | |||||
| <ProjectReference Include="..\gRPCServer\SpotifyService.csproj" /> | |||||
| <ProjectReference Include="..\IdentityProvider\IdentityProvider.csproj" /> | |||||
| </ItemGroup> | |||||
| </Project> | </Project> |
| public class Worker : BackgroundService | public class Worker : BackgroundService | ||||
| { | { | ||||
| private readonly ILogger<Worker> _logger; | private readonly ILogger<Worker> _logger; | ||||
| private HttpClient _httpClient; | |||||
| public Worker(ILogger<Worker> logger) | public Worker(ILogger<Worker> logger) | ||||
| { | { | ||||
| _logger = logger; | _logger = logger; | ||||
| } | } | ||||
| public override Task StartAsync(CancellationToken cancellationToken) | |||||
| { | |||||
| _httpClient = new HttpClient(); | |||||
| return base.StartAsync(cancellationToken); | |||||
| } | |||||
| protected override async Task ExecuteAsync(CancellationToken stoppingToken) | protected override async Task ExecuteAsync(CancellationToken stoppingToken) | ||||
| { | { | ||||
| while (!stoppingToken.IsCancellationRequested) | while (!stoppingToken.IsCancellationRequested) | ||||
| { | { | ||||
| _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); | _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); | ||||
| await Task.Delay(1000, stoppingToken); | |||||
| await Task.Delay(5000, stoppingToken); | |||||
| } | } | ||||
| } | } | ||||
| } | } |