diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index c54d7a7..e778438 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -50,6 +50,7 @@ jobs:
- name: Save the SSH private key to a file
run: |
if [ ! -f ~/.ssh/id_rsa ]; then
+ echo "Save the SSH private key to a file"
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
@@ -58,6 +59,7 @@ jobs:
- name: Add remote server to known_hosts
run: |
if ! grep -q "${{ env.SSH_HOST }}" ~/.ssh/known_hosts; then
+ echo "Add remote server to known_hosts"
ssh-keyscan ${{ env.SSH_HOST }} >> ~/.ssh/known_hosts
fi
diff --git a/src/MvcDemo/Data/DemoDbInitializer.cs b/src/MvcDemo/Data/DemoDbInitializer.cs
deleted file mode 100644
index 722bb84..0000000
--- a/src/MvcDemo/Data/DemoDbInitializer.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using Microsoft.AspNetCore.Identity;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.DependencyInjection;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Demo.Models;
-
-namespace Demo.Data
-{
- public static class DemoDbInitializer
- {
- public const string AdminUserName = "demo";
- public const string AdminInitPassword = "demo@myvas.com";
-
- public static async Task Initialize(IServiceProvider serviceProvider)
- {
- using var db = new DemoDbContext(serviceProvider.GetRequiredService>());
- await EnsureAdminUser(serviceProvider);
- }
-
- private static async Task EnsureAdminUser(IServiceProvider serviceProvider)
- {
- var userManager = serviceProvider.GetRequiredService>();
-
- var user = await userManager.FindByNameAsync(AdminUserName);
- if (user == null)
- {
- user = new IdentityUser()
- {
- UserName = AdminUserName,
- Email = "demo@myvas.com",
- EmailConfirmed = true,
- PhoneNumber = "13800138000",
- PhoneNumberConfirmed = true,
- };
- var result = await userManager.CreateAsync(user, AdminInitPassword);
- if (!result.Succeeded)
- {
- throw new Exception(GetErrorMessage(result));
- }
- }
- }
-
- private static string GetErrorMessage(IdentityResult identityResult)
- {
- var result = "";
-
- foreach (var error in identityResult.Errors)
- {
- result += $"[{error.Code}]{error.Description}" + Environment.NewLine;
- }
- return result;
- }
-
-
- }
-}
diff --git a/src/MvcDemo/Data/DemoDesignTimeDbContextFactory.cs b/src/MvcDemo/Data/DemoDesignTimeDbContextFactory.cs
deleted file mode 100644
index 94d18b2..0000000
--- a/src/MvcDemo/Data/DemoDesignTimeDbContextFactory.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Design;
-
-namespace Demo.Data
-{
- public class DemoDesignTimeDbContextFactory : IDesignTimeDbContextFactory
- {
- public DemoDbContext CreateDbContext(string[] args)
- {
- var optionsBuilder = new DbContextOptionsBuilder();
- optionsBuilder.UseSqlite("Data Source=demo.sqlite");
-
- return new DemoDbContext(optionsBuilder.Options);
- }
- }
-}
diff --git a/src/MvcDemo/Data/HostDatabaseExtensions.cs b/src/MvcDemo/Data/HostDatabaseExtensions.cs
deleted file mode 100644
index fdcfd03..0000000
--- a/src/MvcDemo/Data/HostDatabaseExtensions.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using Microsoft.AspNetCore.Builder;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using System;
-
-namespace Demo.Data;
-
-public static class HostDatabaseExtensions
-{
- public static WebApplication MigrateDatabase(this WebApplication host)
- {
- using (var scope = host.Services.CreateScope())
- {
- var services = scope.ServiceProvider;
-
- try
- {
- var db = services.GetRequiredService();
- db.Database.Migrate();
- }
- catch (Exception ex)
- {
- var logger = services.GetRequiredService>();
- logger.LogError(ex, "An error occurred while migrating the database.");
- }
- }
-
- return host;
- }
-
- public static WebApplication SeedDatabase(this WebApplication host)
- {
- using (var scope = host.Services.CreateScope())
- {
- var services = scope.ServiceProvider;
-
- try
- {
- DemoDbInitializer.Initialize(services).Wait();
- }
- catch (Exception ex)
- {
- var logger = services.GetRequiredService>();
- logger.LogError(ex, "An error occurred while seeding the database.");
- }
- }
-
- return host;
- }
-}
diff --git a/src/MvcDemo/Data/WebApplicationDatabaseExtensions.cs b/src/MvcDemo/Data/WebApplicationDatabaseExtensions.cs
new file mode 100644
index 0000000..65f63be
--- /dev/null
+++ b/src/MvcDemo/Data/WebApplicationDatabaseExtensions.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace Demo.Data;
+
+public static class WebApplicationDatabaseExtensions
+{
+ public static WebApplication MigrateDatabase(this WebApplication app)
+ {
+ using (var scope = app.Services.CreateScope())
+ {
+ var services = scope.ServiceProvider;
+
+ try
+ {
+ var db = services.GetRequiredService();
+ db.Database.Migrate();
+ }
+ catch (Exception ex)
+ {
+ var logger = services.GetRequiredService>();
+ logger.LogError(ex, "An error occurred while migrating the database.");
+ }
+ }
+
+ return app;
+ }
+
+
+ public static WebApplication SeedDatabase(this WebApplication app, string adminUserName, string adminInitPassword)
+ {
+ using (var scope = app.Services.CreateScope())
+ {
+ var services = scope.ServiceProvider;
+
+ try
+ {
+ Task.Run(async () =>
+ {
+ var userManager = services.GetRequiredService>();
+ await EnsureAdminUser(userManager, adminUserName, adminInitPassword);
+ });
+ }
+ catch (Exception ex)
+ {
+ var logger = services.GetRequiredService>();
+ logger.LogError(ex, "An error occurred while seeding the database.");
+ }
+ }
+
+ return app;
+ }
+
+ private static async Task EnsureAdminUser(UserManager userManager, string adminUserName, string adminEmail)
+ {
+ var adminInitPassword = adminEmail;
+
+ var user = await userManager.FindByNameAsync(adminUserName);
+ if (user == null)
+ {
+ user = new IdentityUser()
+ {
+ UserName = adminUserName,
+ Email = adminEmail,
+ EmailConfirmed = true,
+ };
+ var result = await userManager.CreateAsync(user, adminInitPassword);
+ if (!result.Succeeded)
+ {
+ throw new Exception(GetErrorMessage(result));
+ }
+ }
+ }
+
+ private static string GetErrorMessage(IdentityResult identityResult)
+ {
+ var result = "";
+
+ foreach (var error in identityResult.Errors)
+ {
+ result += $"[{error.Code}]{error.Description}" + Environment.NewLine;
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/MvcDemo/Demo.csproj b/src/MvcDemo/Demo.csproj
index 99dbc3f..edda96a 100644
--- a/src/MvcDemo/Demo.csproj
+++ b/src/MvcDemo/Demo.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/src/MvcDemo/HostExtensions.cs b/src/MvcDemo/HostExtensions.cs
index 04219b7..81ac6ac 100644
--- a/src/MvcDemo/HostExtensions.cs
+++ b/src/MvcDemo/HostExtensions.cs
@@ -12,7 +12,7 @@ namespace Demo;
public static class HostExtensions
{
- public static WebApplication ConfigureServices(this WebApplicationBuilder builder)
+ public static WebApplicationBuilder ConfigureServices(this WebApplicationBuilder builder)
{
var Configuration = builder.Configuration;
@@ -91,6 +91,7 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde
{
o.Configuration = Configuration.GetConnectionString("RedisConnection");
})
+ .AddJsapiTicketRedisCacheProvider()
.AddWeixinSite(o =>
{
o.Debug = true; // for this demo for debugging
@@ -105,7 +106,7 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde
})
.AddWeixinEventSink();
- return builder.Build();
+ return builder;
}
public static WebApplication ConfigurePipeline(this WebApplication app)
diff --git a/src/MvcDemo/Migrations/20250323190719_V2.Designer.cs b/src/MvcDemo/Migrations/20250325070817_V3.Designer.cs
similarity index 99%
rename from src/MvcDemo/Migrations/20250323190719_V2.Designer.cs
rename to src/MvcDemo/Migrations/20250325070817_V3.Designer.cs
index d342b94..c1b5614 100644
--- a/src/MvcDemo/Migrations/20250323190719_V2.Designer.cs
+++ b/src/MvcDemo/Migrations/20250325070817_V3.Designer.cs
@@ -11,8 +11,8 @@
namespace Demo.Migrations
{
[DbContext(typeof(DemoDbContext))]
- [Migration("20250323190719_V2")]
- partial class V2
+ [Migration("20250325070817_V3")]
+ partial class V3
{
///
protected override void BuildTargetModel(ModelBuilder modelBuilder)
diff --git a/src/MvcDemo/Migrations/20250323190719_V2.cs b/src/MvcDemo/Migrations/20250325070817_V3.cs
similarity index 57%
rename from src/MvcDemo/Migrations/20250323190719_V2.cs
rename to src/MvcDemo/Migrations/20250325070817_V3.cs
index ee8e147..83e2fc5 100644
--- a/src/MvcDemo/Migrations/20250323190719_V2.cs
+++ b/src/MvcDemo/Migrations/20250325070817_V3.cs
@@ -6,7 +6,7 @@
namespace Demo.Migrations
{
///
- public partial class V2 : Migration
+ public partial class V3 : Migration
{
///
protected override void Up(MigrationBuilder migrationBuilder)
@@ -23,42 +23,51 @@ protected override void Up(MigrationBuilder migrationBuilder)
table: "WeixinSubscribers");
migrationBuilder.DropColumn(
- name: "CreateUnixTime",
+ name: "RowVersion",
table: "WeixinSubscribers");
migrationBuilder.DropColumn(
- name: "RowVersion",
- table: "WeixinSubscribers");
+ name: "CreateUnixTime",
+ table: "WeixinReceivedMessages");
+
+ migrationBuilder.DropColumn(
+ name: "CreateUnixTime",
+ table: "WeixinReceivedEvents");
migrationBuilder.RenameColumn(
- name: "Gender",
+ name: "UnsubscribedTime",
table: "WeixinSubscribers",
- newName: "Sex");
+ newName: "SecurityStamp");
migrationBuilder.RenameColumn(
- name: "NickName",
+ name: "Unsubscribed",
table: "WeixinSubscribers",
- newName: "Nickname");
+ newName: "Subscribed");
migrationBuilder.RenameColumn(
- name: "AvatorImageUrl",
+ name: "SubscribedTime",
table: "WeixinSubscribers",
newName: "HeadImgUrl");
-
+
migrationBuilder.RenameColumn(
- name: "SubscribedTime",
+ name: "Gender",
table: "WeixinSubscribers",
- newName: "SubscribeTime");
+ newName: "UnsubscribeTime");
migrationBuilder.RenameColumn(
- name: "Unsubscribed",
+ name: "AvatorImageUrl",
table: "WeixinSubscribers",
- newName: "Subscribed");
-
- migrationBuilder.RenameColumn(
- name: "UnsubscribedTime",
+ newName: "ConcurrencyStamp");
+
+ migrationBuilder.AlterColumn(
+ name: "OpenId",
table: "WeixinSubscribers",
- newName: "UnsubscribeTime");
+ type: "TEXT",
+ maxLength: 32,
+ nullable: true,
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldMaxLength: 32);
migrationBuilder.AddColumn(
name: "Id",
@@ -67,23 +76,51 @@ protected override void Up(MigrationBuilder migrationBuilder)
nullable: false,
defaultValue: "");
- migrationBuilder.AddColumn(
- name: "SecurityStamp",
+ migrationBuilder.AddColumn(
+ name: "Sex",
table: "WeixinSubscribers",
- type: "TEXT",
+ type: "INTEGER",
nullable: true);
-
- migrationBuilder.AddColumn(
- name: "ConcurrencyStamp",
+
+ migrationBuilder.AddColumn(
+ name: "SubscribeTime",
table: "WeixinSubscribers",
- type: "TEXT",
+ type: "INTEGER",
nullable: true);
-
- migrationBuilder.AddColumn(
- name: "CreateTime",
+
+ migrationBuilder.AlterColumn(
+ name: "ToUserName",
+ table: "WeixinReceivedMessages",
+ type: "TEXT",
+ nullable: true,
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldMaxLength: 32);
+
+ migrationBuilder.AlterColumn(
+ name: "MsgType",
+ table: "WeixinReceivedMessages",
+ type: "TEXT",
+ nullable: true,
+ oldClrType: typeof(string),
+ oldType: "TEXT");
+
+ migrationBuilder.AlterColumn(
+ name: "MsgId",
table: "WeixinReceivedMessages",
type: "INTEGER",
- nullable: true);
+ nullable: true,
+ oldClrType: typeof(long),
+ oldType: "INTEGER");
+
+ migrationBuilder.AlterColumn(
+ name: "FromUserName",
+ table: "WeixinReceivedMessages",
+ type: "TEXT",
+ nullable: true,
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldMaxLength: 32);
migrationBuilder.AddColumn(
name: "ConcurrencyStamp",
@@ -93,16 +130,56 @@ protected override void Up(MigrationBuilder migrationBuilder)
migrationBuilder.AddColumn(
name: "CreateTime",
- table: "WeixinReceivedEvents",
+ table: "WeixinReceivedMessages",
type: "INTEGER",
nullable: true);
+ migrationBuilder.AlterColumn(
+ name: "ToUserName",
+ table: "WeixinReceivedEvents",
+ type: "TEXT",
+ nullable: true,
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldMaxLength: 32);
+
+ migrationBuilder.AlterColumn(
+ name: "MsgType",
+ table: "WeixinReceivedEvents",
+ type: "TEXT",
+ nullable: true,
+ oldClrType: typeof(string),
+ oldType: "TEXT");
+
+ migrationBuilder.AlterColumn(
+ name: "FromUserName",
+ table: "WeixinReceivedEvents",
+ type: "TEXT",
+ nullable: true,
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldMaxLength: 32);
+
+ migrationBuilder.AlterColumn(
+ name: "Event",
+ table: "WeixinReceivedEvents",
+ type: "TEXT",
+ nullable: true,
+ oldClrType: typeof(string),
+ oldType: "TEXT");
+
migrationBuilder.AddColumn(
name: "ConcurrencyStamp",
table: "WeixinReceivedEvents",
type: "TEXT",
nullable: true);
+ migrationBuilder.AddColumn(
+ name: "CreateTime",
+ table: "WeixinReceivedEvents",
+ type: "INTEGER",
+ nullable: true);
+
migrationBuilder.AddPrimaryKey(
name: "PK_WeixinSubscribers",
table: "WeixinSubscribers",
@@ -173,35 +250,71 @@ protected override void Down(MigrationBuilder migrationBuilder)
name: "PK_WeixinSubscribers",
table: "WeixinSubscribers");
- migrationBuilder.RenameColumn(
+ migrationBuilder.DropColumn(
+ name: "Id",
+ table: "WeixinSubscribers");
+
+ migrationBuilder.DropColumn(
name: "Sex",
- table: "WeixinSubscribers",
- newName: "Gender");
+ table: "WeixinSubscribers");
- migrationBuilder.RenameColumn(
+ migrationBuilder.DropColumn(
name: "SubscribeTime",
- table: "WeixinSubscribers",
- newName: "SubscribedTime");
+ table: "WeixinSubscribers");
+
+ migrationBuilder.DropColumn(
+ name: "ConcurrencyStamp",
+ table: "WeixinReceivedMessages");
+
+ migrationBuilder.DropColumn(
+ name: "CreateTime",
+ table: "WeixinReceivedMessages");
+
+ migrationBuilder.DropColumn(
+ name: "ConcurrencyStamp",
+ table: "WeixinReceivedEvents");
+
+ migrationBuilder.DropColumn(
+ name: "CreateTime",
+ table: "WeixinReceivedEvents");
migrationBuilder.RenameColumn(
name: "UnsubscribeTime",
table: "WeixinSubscribers",
- newName: "UnsubscribedTime");
+ newName: "Gender");
migrationBuilder.RenameColumn(
name: "Subscribed",
table: "WeixinSubscribers",
newName: "Unsubscribed");
- migrationBuilder.DropColumn(
+ migrationBuilder.RenameColumn(
name: "SecurityStamp",
- table: "WeixinSubscribers");
+ table: "WeixinSubscribers",
+ newName: "UnsubscribedTime");
migrationBuilder.RenameColumn(
name: "HeadImgUrl",
table: "WeixinSubscribers",
+ newName: "SubscribedTime");
+
+ migrationBuilder.RenameColumn(
+ name: "ConcurrencyStamp",
+ table: "WeixinSubscribers",
newName: "AvatorImageUrl");
+ migrationBuilder.AlterColumn(
+ name: "OpenId",
+ table: "WeixinSubscribers",
+ type: "TEXT",
+ maxLength: 32,
+ nullable: false,
+ defaultValue: "",
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldMaxLength: 32,
+ oldNullable: true);
+
migrationBuilder.AddColumn(
name: "AppId",
table: "WeixinSubscribers",
@@ -215,6 +328,48 @@ protected override void Down(MigrationBuilder migrationBuilder)
rowVersion: true,
nullable: true);
+ migrationBuilder.AlterColumn(
+ name: "ToUserName",
+ table: "WeixinReceivedMessages",
+ type: "TEXT",
+ maxLength: 32,
+ nullable: false,
+ defaultValue: "",
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldNullable: true);
+
+ migrationBuilder.AlterColumn(
+ name: "MsgType",
+ table: "WeixinReceivedMessages",
+ type: "TEXT",
+ nullable: false,
+ defaultValue: "",
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldNullable: true);
+
+ migrationBuilder.AlterColumn(
+ name: "MsgId",
+ table: "WeixinReceivedMessages",
+ type: "INTEGER",
+ nullable: false,
+ defaultValue: 0L,
+ oldClrType: typeof(long),
+ oldType: "INTEGER",
+ oldNullable: true);
+
+ migrationBuilder.AlterColumn(
+ name: "FromUserName",
+ table: "WeixinReceivedMessages",
+ type: "TEXT",
+ maxLength: 32,
+ nullable: false,
+ defaultValue: "",
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldNullable: true);
+
migrationBuilder.AddColumn(
name: "CreateUnixTime",
table: "WeixinReceivedMessages",
@@ -222,6 +377,48 @@ protected override void Down(MigrationBuilder migrationBuilder)
nullable: false,
defaultValue: 0L);
+ migrationBuilder.AlterColumn(
+ name: "ToUserName",
+ table: "WeixinReceivedEvents",
+ type: "TEXT",
+ maxLength: 32,
+ nullable: false,
+ defaultValue: "",
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldNullable: true);
+
+ migrationBuilder.AlterColumn(
+ name: "MsgType",
+ table: "WeixinReceivedEvents",
+ type: "TEXT",
+ nullable: false,
+ defaultValue: "",
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldNullable: true);
+
+ migrationBuilder.AlterColumn(
+ name: "FromUserName",
+ table: "WeixinReceivedEvents",
+ type: "TEXT",
+ maxLength: 32,
+ nullable: false,
+ defaultValue: "",
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldNullable: true);
+
+ migrationBuilder.AlterColumn(
+ name: "Event",
+ table: "WeixinReceivedEvents",
+ type: "TEXT",
+ nullable: false,
+ defaultValue: "",
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldNullable: true);
+
migrationBuilder.AddColumn(
name: "CreateUnixTime",
table: "WeixinReceivedEvents",
diff --git a/src/MvcDemo/Program.cs b/src/MvcDemo/Program.cs
index 292d50e..548c2ec 100644
--- a/src/MvcDemo/Program.cs
+++ b/src/MvcDemo/Program.cs
@@ -12,16 +12,17 @@
var assembly = typeof(Program).Assembly;
var assemblyName = assembly.GetName().Name;
-var assemblyVersion = assembly.GetCustomAttribute()?.InformationalVersion;
-Log.Information($"{assemblyName} {assemblyVersion} starting up...");
+var assemblyVersion = assembly.GetName().Version?.ToString()
+ ?? assembly.GetCustomAttribute()?.InformationalVersion;
+Log.Information($"{assemblyName} v{assemblyVersion} starting up...");
try
{
var builder = WebApplication.CreateBuilder(args);
- var app = builder.ConfigureServices()
+ var app = builder.ConfigureServices().Build()
.MigrateDatabase()
- .SeedDatabase()
+ .SeedDatabase("demo", "demo@myvas.com")
.ConfigurePipeline();
app.Run();
@@ -34,6 +35,6 @@
}
finally
{
- Log.Information($"{assemblyName} {assemblyVersion} shutdown.");
+ Log.Information($"{assemblyName} v{assemblyVersion} shutdown.");
Log.CloseAndFlush();
}
diff --git a/src/MvcDemo/Views/Home/Index.cshtml b/src/MvcDemo/Views/Home/Index.cshtml
index dec7fea..71e9c31 100644
--- a/src/MvcDemo/Views/Home/Index.cshtml
+++ b/src/MvcDemo/Views/Home/Index.cshtml
@@ -1,16 +1,17 @@
@using System.Reflection
@{
- ViewData["Title"] = "Demo";
-
var assembly = typeof(Program).Assembly;
var assemblyName = assembly.GetName().Name;
- var assemblyVersion = assembly.GetCustomAttribute()?.InformationalVersion
- ?? assembly.GetName().Version.ToString();
+ var assemblyVersion = assembly.GetName().Version?.ToString()
+ ?? assembly.GetCustomAttribute()?.InformationalVersion;
+
+ ViewData["Title"] = assemblyName;
}
-@ViewData["Title"] @(assemblyVersion)
+@assemblyName v@assemblyVersion
-这是一组github开源项目的演示网站。本网站源代码在此:Myvas.AspNetCore.Authentication.Demo
+这是一组github开源项目的演示网站。本网站源代码在此:Myvas.AspNetCore.Authentication.Demo
- WeixinOpen (open.weixin.qq.com)
- WeixinAuth (mp.weixin.qq.com)
@@ -24,12 +25,14 @@
触发身份验证
(1)点击UserInfo触发Challenge进入登录页面。
--- 或 ---
-(2)点击这里展示一个网址二维码,用手机扫码进入。
+(2)点击这里展示一个网址二维码,用手机扫码进入。
--- 或 ---
(3)直接扫码进入微信公众号测试,然后点击菜单[UserInfo]
-
+
@@ -76,5 +79,6 @@
\ No newline at end of file