| @@ -0,0 +1,17 @@ | |||
| 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; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| 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; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| 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; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| 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; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| 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 | |||
| { | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| 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); | |||
| } | |||
| } | |||
| @@ -10,6 +10,7 @@ | |||
| <PackageReference Include="Grpc.AspNetCore" Version="2.40.0" /> | |||
| <PackageReference Include="Grpc.AspNetCore.Web" Version="2.47.0" /> | |||
| <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="protobuf-net.Grpc" Version="1.0.171" /> | |||
| <PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" /> | |||
| @@ -24,8 +25,4 @@ | |||
| <ProjectReference Include="..\NemAnCore\NemAnBlazor.csproj" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Folder Include="Services\" /> | |||
| </ItemGroup> | |||
| </Project> | |||
| @@ -0,0 +1,10 @@ | |||
| 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!; | |||
| } | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| 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; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| 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; } | |||
| } | |||
| } | |||
| @@ -5,6 +5,7 @@ using ProtoBuf.Grpc.Server; | |||
| using Microsoft.Extensions.Options; | |||
| using GrpcShared.DTO.Auth; | |||
| using Blazored.LocalStorage; | |||
| using IdentityProvider.Models; | |||
| var builder = WebApplication.CreateBuilder(args); | |||
| #if DEBUG | |||
| @@ -25,6 +26,7 @@ builder.Services.AddHttpClient("HttpClient", c => | |||
| }); | |||
| builder.Services.AddOptions(); | |||
| builder.Services.AddSingleton<SpotifyDbConfig>(); | |||
| // 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 | |||
| builder.Services.AddControllersWithViews(); | |||
| @@ -38,7 +40,12 @@ builder.Services.AddCodeFirstGrpcReflection(); | |||
| builder.Services.AddBlazoredLocalStorage(); | |||
| //call spotify api | |||
| 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(); | |||
| @@ -0,0 +1,97 @@ | |||
| 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(); | |||
| } | |||
| } | |||
| } | |||
| @@ -16,5 +16,11 @@ | |||
| "RedirectURI": "https://localhost:44342/callback", | |||
| "Scope": "user-read-currently-playing user-read-email user-library-modify user-top-read user-read-private", | |||
| "ClientSecret": "ea752433d0774fad87fab5c1ee788c8d" | |||
| }, | |||
| "SpotifyDb": { | |||
| "ConnectionString": "mongodb://127.0.0.1:27017", | |||
| "DatabaseName": "spotifyDb", | |||
| "UserCollection": "Users", | |||
| "TracksCollection": "Tracks" | |||
| } | |||
| } | |||
| @@ -8,6 +8,7 @@ | |||
| @inject IStatsClientService spotifyService | |||
| @inject ITrackClientService trackService | |||
| @inject IAuthClientService AuthService | |||
| @inject IIdentityClientService identityService | |||
| <h3>Home</h3> | |||
| @@ -68,5 +69,6 @@ | |||
| } | |||
| } | |||
| } | |||
| @@ -28,6 +28,7 @@ builder.Services.AddAuthorizationCore(); | |||
| builder.Services.AddScoped<ITrackClientService, TrackClientService>(); | |||
| builder.Services.AddScoped<IAuthClientService, AuthClientService>(); | |||
| builder.Services.AddScoped<IStatsClientService, StatsClientService>(); | |||
| builder.Services.AddScoped<IIdentityClientService, IdentityClientService>(); | |||
| builder.Services.AddBlazoredLocalStorage(); | |||
| builder.Services.AddScoped<AuthenticationStateProvider, AuthClientService>(); | |||
| builder.Services.AddAuthorizationCore(); | |||
| @@ -0,0 +1,52 @@ | |||
| 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| 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); | |||
| } | |||
| } | |||