diff --git a/.env.example b/.env.example index c5849686..d2c7391b 100644 --- a/.env.example +++ b/.env.example @@ -74,5 +74,6 @@ POSTGRES_PORT=5432 MEILI_HOST="http://meilisearch:7700" MEILI_MASTER_KEY="ghvjkgisbgkbgskegblfqbgjkebbhgwkjfb" +RABBITMQ_HOST=rabbitmq:5672 RABBITMQ_DEFAULT_USER=kyoo RABBITMQ_DEFAULT_PASS=aohohunuhouhuhhoahothonseuhaoensuthoaentsuhha diff --git a/back/Kyoo.sln b/back/Kyoo.sln index db805de4..10bf4f8b 100644 --- a/back/Kyoo.sln +++ b/back/Kyoo.sln @@ -1,4 +1,5 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kyoo.Core", "src\Kyoo.Core\Kyoo.Core.csproj", "{0F8275B6-C7DD-42DF-A168-755C81B1C329}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Abstractions", "src\Kyoo.Abstractions\Kyoo.Abstractions.csproj", "{BAB2CAE1-AC28-4509-AA3E-8DC75BD59220}" @@ -13,6 +14,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Host", "src\Kyoo.Host\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Meilisearch", "src\Kyoo.Meilisearch\Kyoo.Meilisearch.csproj", "{F8E6018A-FD51-40EB-99FF-A26BA59F2762}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{787FB205-9D7E-4946-AFE0-BD68E286F569}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.RabbitMq", "src\Kyoo.RabbitMq\Kyoo.RabbitMq.csproj", "{B97AD4A8-E6E6-41CD-87DF-5F1326FD7198}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -59,5 +64,18 @@ Global {F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Debug|Any CPU.Build.0 = Debug|Any CPU {F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Release|Any CPU.ActiveCfg = Release|Any CPU {F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Release|Any CPU.Build.0 = Release|Any CPU + {44F2208F-C015-4A01-8D6A-20F82437AFDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44F2208F-C015-4A01-8D6A-20F82437AFDB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44F2208F-C015-4A01-8D6A-20F82437AFDB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44F2208F-C015-4A01-8D6A-20F82437AFDB}.Release|Any CPU.Build.0 = Release|Any CPU + {B97AD4A8-E6E6-41CD-87DF-5F1326FD7198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B97AD4A8-E6E6-41CD-87DF-5F1326FD7198}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B97AD4A8-E6E6-41CD-87DF-5F1326FD7198}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B97AD4A8-E6E6-41CD-87DF-5F1326FD7198}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {0C8AA7EA-E723-4532-852F-35AA4E8AFED5} = {FEAE1B0E-D797-470F-9030-0EF743575ECC} + {44F2208F-C015-4A01-8D6A-20F82437AFDB} = {BB39454F-53E4-4238-9659-A39638496FB3} + {B97AD4A8-E6E6-41CD-87DF-5F1326FD7198} = {787FB205-9D7E-4946-AFE0-BD68E286F569} EndGlobalSection EndGlobal diff --git a/back/src/Kyoo.Host/Kyoo.Host.csproj b/back/src/Kyoo.Host/Kyoo.Host.csproj index dfed95e7..418d06f9 100644 --- a/back/src/Kyoo.Host/Kyoo.Host.csproj +++ b/back/src/Kyoo.Host/Kyoo.Host.csproj @@ -26,6 +26,7 @@ + diff --git a/back/src/Kyoo.Host/PluginsStartup.cs b/back/src/Kyoo.Host/PluginsStartup.cs index ddd6a57b..40f4c3d1 100644 --- a/back/src/Kyoo.Host/PluginsStartup.cs +++ b/back/src/Kyoo.Host/PluginsStartup.cs @@ -27,6 +27,7 @@ using Kyoo.Core; using Kyoo.Host.Controllers; using Kyoo.Meiliseach; using Kyoo.Postgresql; +using Kyoo.RabbitMq; using Kyoo.Swagger; using Kyoo.Utils; using Microsoft.AspNetCore.Builder; @@ -66,6 +67,7 @@ public class PluginsStartup typeof(AuthenticationModule), typeof(PostgresModule), typeof(MeilisearchModule), + typeof(RabbitMqModule), typeof(SwaggerModule) ); } diff --git a/back/src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj b/back/src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj new file mode 100644 index 00000000..382b5b7c --- /dev/null +++ b/back/src/Kyoo.RabbitMq/Kyoo.RabbitMq.csproj @@ -0,0 +1,16 @@ + + + enable + enable + Kyoo.RabbitMq + + + + + + + + + + + diff --git a/back/src/Kyoo.RabbitMq/RabbitMqModule.cs b/back/src/Kyoo.RabbitMq/RabbitMqModule.cs new file mode 100644 index 00000000..2e7031db --- /dev/null +++ b/back/src/Kyoo.RabbitMq/RabbitMqModule.cs @@ -0,0 +1,53 @@ +// Kyoo - A portable and vast media library solution. +// Copyright (c) Kyoo. +// +// See AUTHORS.md and LICENSE file in the project root for full license information. +// +// Kyoo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// Kyoo is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Kyoo. If not, see . + +using Autofac; +using Kyoo.Abstractions.Controllers; +using Microsoft.Extensions.Configuration; +using RabbitMQ.Client; + +namespace Kyoo.RabbitMq; + +public class RabbitMqModule(IConfiguration configuration) : IPlugin +{ + /// + public string Name => "RabbitMq"; + + /// + public void Configure(ContainerBuilder builder) + { + builder + .Register( + (_) => + { + ConnectionFactory factory = + new() + { + UserName = configuration.GetValue("RABBITMQ_DEFAULT_USER", "guest"), + Password = configuration.GetValue("RABBITMQ_DEFAULT_USER", "guest"), + HostName = configuration.GetValue("RABBITMQ_HOST", "rabbitmq:5672"), + }; + + return factory.CreateConnection(); + } + ) + .AsSelf() + .SingleInstance(); + builder.RegisterType().AsSelf().SingleInstance().AutoActivate(); + } +} diff --git a/back/src/Kyoo.RabbitMq/RabbitProducer.cs b/back/src/Kyoo.RabbitMq/RabbitProducer.cs new file mode 100644 index 00000000..e434989a --- /dev/null +++ b/back/src/Kyoo.RabbitMq/RabbitProducer.cs @@ -0,0 +1,90 @@ +// Kyoo - A portable and vast media library solution. +// Copyright (c) Kyoo. +// +// See AUTHORS.md and LICENSE file in the project root for full license information. +// +// Kyoo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// Kyoo is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Kyoo. If not, see . + +using System.Text; +using System.Text.Json; +using Kyoo.Abstractions.Controllers; +using Kyoo.Abstractions.Models; +using RabbitMQ.Client; + +namespace Kyoo.RabbitMq; + +public class RabbitProducer +{ + private readonly IModel _channel; + + public RabbitProducer(IConnection rabbitConnection) + { + _channel = rabbitConnection.CreateModel(); + + _channel.ExchangeDeclare(exchange: "events.resource.collection", type: ExchangeType.Fanout); + IRepository.OnCreated += _Publish("events.resource.collection", "created"); + IRepository.OnEdited += _Publish("events.resource.collection", "edited"); + IRepository.OnDeleted += _Publish("events.resource.collection", "deleted"); + + _channel.ExchangeDeclare(exchange: "events.resource.movie", type: ExchangeType.Fanout); + IRepository.OnCreated += _Publish("events.resource.movie", "created"); + IRepository.OnEdited += _Publish("events.resource.movie", "edited"); + IRepository.OnDeleted += _Publish("events.resource.movie", "deleted"); + + _channel.ExchangeDeclare(exchange: "events.resource.show", type: ExchangeType.Fanout); + IRepository.OnCreated += _Publish("events.resource.show", "created"); + IRepository.OnEdited += _Publish("events.resource.show", "edited"); + IRepository.OnDeleted += _Publish("events.resource.show", "deleted"); + + _channel.ExchangeDeclare(exchange: "events.resource.season", type: ExchangeType.Fanout); + IRepository.OnCreated += _Publish("events.resource.season", "created"); + IRepository.OnEdited += _Publish("events.resource.season", "edited"); + IRepository.OnDeleted += _Publish("events.resource.season", "deleted"); + + _channel.ExchangeDeclare(exchange: "events.resource.episode", type: ExchangeType.Fanout); + IRepository.OnCreated += _Publish("events.resource.episode", "created"); + IRepository.OnEdited += _Publish("events.resource.episode", "edited"); + IRepository.OnDeleted += _Publish("events.resource.episode", "deleted"); + + _channel.ExchangeDeclare(exchange: "events.resource.studio", type: ExchangeType.Fanout); + IRepository.OnCreated += _Publish("events.resource.studio", "created"); + IRepository.OnEdited += _Publish("events.resource.studio", "edited"); + IRepository.OnDeleted += _Publish("events.resource.studio", "deleted"); + + _channel.ExchangeDeclare(exchange: "events.resource.user", type: ExchangeType.Fanout); + IRepository.OnCreated += _Publish("events.resource.user", "created"); + IRepository.OnEdited += _Publish("events.resource.user", "edited"); + IRepository.OnDeleted += _Publish("events.resource.user", "deleted"); + } + + private IRepository.ResourceEventHandler _Publish(string exchange, string action) + where T : IResource, IQuery + { + return (T resource) => + { + var message = new + { + Action = action, + Type = typeof(T).Name.ToLowerInvariant(), + Value = resource, + }; + _channel.BasicPublish( + exchange, + routingKey: string.Empty, + body: Encoding.UTF8.GetBytes(JsonSerializer.Serialize(message)) + ); + return Task.CompletedTask; + }; + } +}