| 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; } | |||||
| } | |||||
| } |
| 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; } | |||||
| } | |||||
| } |
| 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 | |||||
| { | |||||
| } | |||||
| } |
| 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); | |||||
| } | |||||
| } |
| <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 ConnString { get; set; } = null!; | |||||
| public string DBName { get; set; } = null!; | |||||
| public string UserCollection { get; set; } = null!; | |||||
| public string TrackCollection { 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; } | |||||
| } | |||||
| } |
| 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; } | |||||
| } | |||||
| } |
| 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; | |||||
| 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(); | ||||
| 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.ConnString); | |||||
| var mongoDbContext = mongoClient.GetDatabase(spotifyDbConfig.Value.DBName); | |||||
| _userCollection = mongoDbContext.GetCollection<UserModel>(spotifyDbConfig.Value.UserCollection); | |||||
| _trackCollection = mongoDbContext.GetCollection<TrackModel>(spotifyDbConfig.Value.TrackCollection); | |||||
| } | |||||
| public async Task<List<UserResponse>> ListUsersAsync(VoidMessage msg) | |||||
| { | |||||
| List<UserModel> users = await _userCollection.Find(_ => true).ToListAsync(); | |||||
| //map users to match the grpc response | |||||
| return users.Select(u => new UserResponse | |||||
| { | |||||
| Id = u.Id, | |||||
| Token = u.Token | |||||
| }).ToList(); | |||||
| } | |||||
| public async Task<UserResponse> GetTokenByIdAsync(DbRequestMessage request) | |||||
| { | |||||
| UserModel user = await _userCollection.Find(u => u.Id == request.Id).FirstOrDefaultAsync(); | |||||
| return new UserResponse | |||||
| { | |||||
| Id = user.Id, | |||||
| Token = user.Token | |||||
| }; | |||||
| } | |||||
| 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 | |||||
| }; | |||||
| } | |||||
| public async Task<VoidMessage> SaveTrackAsync(SaveTrackRequest track) | |||||
| { | |||||
| TrackModel trackModel = new() | |||||
| { | |||||
| Title = track.Title, | |||||
| Album = track.Album, | |||||
| Artist = track.Artist, | |||||
| UserId = track.UserId | |||||
| }; | |||||
| //first check if there's already a song in the db, if yes, update the row | |||||
| var song = await _trackCollection.FindAsync(x => x.UserId == track.UserId); | |||||
| if (song != null) await _trackCollection.ReplaceOneAsync(x => x.UserId == track.UserId, trackModel); | |||||
| else await _trackCollection.InsertOneAsync(trackModel); | |||||
| return new VoidMessage(); | |||||
| } | |||||
| public async Task<VoidMessage> SaveUserAsync(UserResponse user) | |||||
| { | |||||
| await _userCollection.InsertOneAsync(new UserModel | |||||
| { | |||||
| Id = user.Id, | |||||
| Token = user.Token | |||||
| }); | |||||
| return new VoidMessage(); | |||||
| } | |||||
| public async Task<VoidMessage> DeleteTrackAsync(DbRequestMessage request) | |||||
| { | |||||
| await _trackCollection.DeleteOneAsync(x => x.Id == request.Id); | |||||
| return new VoidMessage(); | |||||
| } | |||||
| public async Task<VoidMessage> DeleteUserAsync(DbRequestMessage user) | |||||
| { | |||||
| await _userCollection.DeleteOneAsync(x => x.Id == user.Id); | |||||
| return new VoidMessage(); | |||||
| } | |||||
| } | |||||
| } |
| "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" | |||||
| } | } | ||||
| } | } |
| @inject IStatsClientService spotifyService | @inject IStatsClientService spotifyService | ||||
| @inject ITrackClientService trackService | @inject ITrackClientService trackService | ||||
| @inject IAuthClientService AuthService | @inject IAuthClientService AuthService | ||||
| @inject IIdentityClientService identityService | |||||
| <h3>Home</h3> | <h3>Home</h3> | ||||
| } | } | ||||
| } | } | ||||
| } | } |
| 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); | |||||
| } | |||||
| } | |||||
| } |
| 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); | |||||
| } | |||||
| } |