Compare commits
7 changed files with 68 additions and 67 deletions
21
.github/workflows/docker-publish-backend.yml
vendored
21
.github/workflows/docker-publish-backend.yml
vendored
|
|
@ -8,7 +8,8 @@ on:
|
|||
|
||||
jobs:
|
||||
production-deploy:
|
||||
runs-on: ubuntu-26.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# 1. Récupération du code
|
||||
- name: Check out the repo
|
||||
|
|
@ -24,24 +25,8 @@ jobs:
|
|||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Set up .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
- name: Workloads restore
|
||||
run: dotnet workload restore
|
||||
- name: Test
|
||||
env:
|
||||
TEST_FAILED: ""
|
||||
run: make test || export TEST_FAILED=$?
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: my-artifact
|
||||
path: test-reports/**/*
|
||||
- name: Fail if tests failed
|
||||
if: ${{ env.TEST_FAILED != '' && env.TEST_FAILED != '0' }}
|
||||
run: exit $TEST_FAILED
|
||||
|
||||
run: dotnet test --no-build --verbosity normal --filter="Category!=Platform-Android"
|
||||
# 4. Build et Push de l'image de production finale
|
||||
- name: Build and push production image
|
||||
uses: docker/build-push-action@v7
|
||||
|
|
|
|||
|
|
@ -29,11 +29,6 @@
|
|||
|
||||
### Fixed
|
||||
|
||||
* [PostIt.Tests] Les tests du bandeau de session utilisent le bandeau
|
||||
réellement monté dans la fenêtre headless, au lieu d'un contrôle détaché.
|
||||
Les fenêtres sont fermées après chaque test et les quatre boutons
|
||||
de navigation sont vérifiés.
|
||||
|
||||
* [PostIt] Les liens Markdown des pièces jointes utilisent désormais
|
||||
le même filtre de nom que l'upload serveur, avant l'encodage URL.
|
||||
Le libellé conserve le nom original, notamment ses accents.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,36 @@
|
|||
# Utilisation de l'image globale pour le build et l'exécution
|
||||
FROM pazof/yavsc-build-env:debian13-dotnet10-android36-jdk21-v4 AS build-env
|
||||
FROM pazof/yavsc-build-env:debian12-dotnet10-android36-v1 AS build-env
|
||||
WORKDIR /src
|
||||
|
||||
|
||||
# 1. Copie de la solution
|
||||
COPY *.sln ./
|
||||
COPY Directory.*.props ./
|
||||
|
||||
# 2. Copie de l'intégralité des fichiers de projets (.csproj) pour la restauration
|
||||
COPY src/Yavsc.Org/*.csproj ./src/Yavsc.Org/
|
||||
COPY src/Yavsc.Abstract/*.csproj ./src/Yavsc.Abstract/
|
||||
COPY src/Yavsc.Server/*.csproj ./src/Yavsc.Server/
|
||||
COPY src/Yavsc.Api/*.csproj ./src/Yavsc.Api/
|
||||
COPY src/Yavsc.Blogs/*.csproj ./src/Yavsc.Blogs/
|
||||
COPY src/cli/*.csproj ./src/cli/
|
||||
COPY src/Yavsc.Org.Tests/*.csproj ./src/Yavsc.Org.Tests/
|
||||
|
||||
# Projets associés à PostIt
|
||||
COPY src/PostIt/PostIt/*.csproj ./src/PostIt/PostIt/
|
||||
COPY src/PostIt/PostIt.Android/*.csproj ./src/PostIt/PostIt.Android/
|
||||
COPY src/PostIt/PostIt.Browser/*.csproj ./src/PostIt/PostIt.Browser/
|
||||
COPY src/PostIt/PostIt.Desktop/*.csproj ./src/PostIt/PostIt.Desktop/
|
||||
|
||||
# 4. Copie de l'intégralité du code source
|
||||
COPY . .
|
||||
RUN dotnet workload restore
|
||||
|
||||
# 4. Restauration des dépendances pour tous les projets
|
||||
RUN dotnet restore
|
||||
|
||||
# 5. Copie de la totalité du code source (filtrée par votre .dockerignore)
|
||||
COPY . .
|
||||
|
||||
# 6. Compilation du serveur Web principal
|
||||
RUN dotnet build src/Yavsc.Org/Yavsc.Org.csproj -c Release --no-restore -clp:ErrorsOnly
|
||||
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -5,6 +5,8 @@ DESTDIR=/tmp/yavsc
|
|||
APP_PATH=srv/www/yavsc
|
||||
APP_FULL_PATH=$(DESTDIR)/$(APP_PATH)
|
||||
|
||||
include .env
|
||||
|
||||
all:
|
||||
dotnet build --nologo
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Headless.XUnit;
|
||||
using Avalonia.Styling;
|
||||
using Avalonia.VisualTree;
|
||||
using PostIt.ViewModels;
|
||||
using PostIt.Views;
|
||||
|
|
@ -22,36 +23,41 @@ namespace PostIt.Tests;
|
|||
/// <c>new Window().Show()</c> throws because the harness has not
|
||||
/// been initialised for that thread.</para>
|
||||
///
|
||||
/// <para>These tests set the DataContext directly on the banner
|
||||
/// mounted in the window. The production code path is exercised
|
||||
/// <para>The session banner's <c>DataContext</c> is not wired in
|
||||
/// these tests: <c>App.OnFrameworkInitializationCompleted</c> is
|
||||
/// not called in a unit test, so we set the DataContext on the
|
||||
/// banner directly. The production code path is exercised
|
||||
/// end-to-end by the manual launch, not here.</para>
|
||||
/// </summary>
|
||||
public class SessionStatusBannerTests : IDisposable
|
||||
public class SessionStatusBannerTests
|
||||
{
|
||||
private MainWindow? _window;
|
||||
|
||||
[AvaloniaFact]
|
||||
public void Banner_renders_four_buttons_in_the_visual_tree()
|
||||
public void Banner_renders_three_buttons_in_the_visual_tree()
|
||||
{
|
||||
SessionStatusBanner banner = CreateBanner();
|
||||
MainWindow window = new MainWindow();
|
||||
window.Show();
|
||||
|
||||
var buttons = banner.GetVisualDescendants()
|
||||
var buttons = window.GetVisualDescendants()
|
||||
.OfType<Button>()
|
||||
.ToList();
|
||||
|
||||
Assert.Equal(4, buttons.Count);
|
||||
// Three buttons, named by their content text: Se
|
||||
// déconnecter, Se connecter, Paramètres. If any one is
|
||||
// missing, the user has no way to trigger the
|
||||
// corresponding navigation event.
|
||||
Assert.True(buttons.Count>=4);
|
||||
Assert.Contains(buttons, b => b.Content as string == "Se déconnecter");
|
||||
Assert.Contains(buttons, b => b.Content as string == "Se connecter");
|
||||
Assert.Contains(buttons, b => b.Content as string == "Paramètres");
|
||||
Assert.Contains(buttons, b => b.Content as string == "À propos");
|
||||
}
|
||||
|
||||
[AvaloniaFact]
|
||||
public void Banner_login_button_is_visible_when_logged_out()
|
||||
{
|
||||
SessionStatusBanner banner = CreateBanner();
|
||||
MainWindow window = new MainWindow();
|
||||
window.Show();
|
||||
|
||||
var login = banner.GetVisualDescendants()
|
||||
var login = window.GetVisualDescendants()
|
||||
.OfType<Button>()
|
||||
.Single(b => b.Content as string == "Se connecter");
|
||||
|
||||
|
|
@ -63,9 +69,12 @@ public class SessionStatusBannerTests : IDisposable
|
|||
[AvaloniaFact]
|
||||
public void Banner_logout_button_is_hidden_when_logged_out()
|
||||
{
|
||||
SessionStatusBanner banner = CreateBanner();
|
||||
SessionStatusBanner banner = CreateBanner();
|
||||
|
||||
Assert.False((banner.DataContext as SessionStatusViewModel)!
|
||||
.IsLoggedIn); // VM default
|
||||
|
||||
|
||||
Assert.False(Assert.IsType<SessionStatusViewModel>(banner.DataContext).IsLoggedIn);
|
||||
var logout = banner.GetVisualDescendants()
|
||||
.OfType<Button>()
|
||||
.Single(b => b.Content as string == "Se déconnecter");
|
||||
|
|
@ -82,23 +91,22 @@ public class SessionStatusBannerTests : IDisposable
|
|||
.OfType<Button>()
|
||||
.Single(b => b.Content as string == "Paramètres");
|
||||
|
||||
// Paramètres has no IsVisible binding and is always shown.
|
||||
// Paramètres is the only button with no IsVisible
|
||||
// binding — always shown. The user's only path to the
|
||||
// settings page goes through this button.
|
||||
Assert.True(settings.IsVisible);
|
||||
}
|
||||
|
||||
private SessionStatusBanner CreateBanner()
|
||||
private static SessionStatusBanner CreateBanner()
|
||||
{
|
||||
_window = new MainWindow();
|
||||
_window.Show();
|
||||
var banner = _window.GetVisualDescendants()
|
||||
.OfType<SessionStatusBanner>()
|
||||
.Single();
|
||||
banner.DataContext = new SessionStatusViewModel();
|
||||
MainWindow window = new MainWindow();
|
||||
window.Show();
|
||||
var banner = new SessionStatusBanner();
|
||||
var status = new SessionStatusViewModel();
|
||||
banner.DataContext = status;
|
||||
return banner;
|
||||
}
|
||||
|
||||
public void Dispose() => _window?.Close();
|
||||
|
||||
[AvaloniaFact]
|
||||
public void Banner_session_label_reflects_DataContext()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -44,6 +44,10 @@ public partial class App : Application, ILiveView, IVMPusherApp
|
|||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
#if DEBUG
|
||||
this.AttachDeveloperTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Identity;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Yavsc.Abstract.Identity;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Server.Helpers;
|
||||
|
|
@ -20,20 +19,14 @@ namespace Yavsc.Controllers
|
|||
private readonly RoleManager<IdentityRole> _roleManager;
|
||||
|
||||
private readonly ApplicationDbContext _dbContext;
|
||||
private readonly SiteSettings _siteSettings;
|
||||
private readonly ILogger<AdministrationController> _logger;
|
||||
|
||||
public AdministrationController(UserManager<ApplicationUser> userManager,
|
||||
RoleManager<IdentityRole> roleManager,
|
||||
ApplicationDbContext context,
|
||||
IOptions<SiteSettings> siteSettings,
|
||||
ILoggerFactory loggerFactory)
|
||||
ApplicationDbContext context)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_roleManager = roleManager;
|
||||
this._dbContext = context;
|
||||
this._siteSettings = siteSettings.Value;
|
||||
this._logger = loggerFactory.CreateLogger<AdministrationController>();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -99,16 +92,6 @@ namespace Yavsc.Controllers
|
|||
}
|
||||
|
||||
var user = await _userManager.FindByIdAsync(User.GetUserId());
|
||||
|
||||
var email = user.Email;
|
||||
var isOk = email == _siteSettings.Owner?.EMail || _siteSettings.Admin?.EMail == email;
|
||||
if (!isOk)
|
||||
{
|
||||
if (_siteSettings.Owner==null && _siteSettings.Admin==null)
|
||||
_logger.LogError("No owner or admin is set in the site settings.");
|
||||
return Forbid();
|
||||
}
|
||||
|
||||
// check all user groups exist
|
||||
if (!await EnsureRoleList())
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue