Results site in .NET Core
This commit is contained in:
parent
879e5bc477
commit
985af62bb1
42
docker-compose-windows.yml
Normal file
42
docker-compose-windows.yml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
version: "3.2"
|
||||||
|
|
||||||
|
services:
|
||||||
|
vote:
|
||||||
|
build:
|
||||||
|
context: ./vote/dotnet
|
||||||
|
ports:
|
||||||
|
- "5000:80"
|
||||||
|
depends_on:
|
||||||
|
- message-queue
|
||||||
|
|
||||||
|
result:
|
||||||
|
build:
|
||||||
|
context: ./result/dotnet
|
||||||
|
ports:
|
||||||
|
- "5001:80"
|
||||||
|
environment:
|
||||||
|
- "Data:ConnectionString=Server=db;Port=4000;Database=votes;User=root;SslMode=None"
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
|
||||||
|
worker:
|
||||||
|
build:
|
||||||
|
context: ./worker/dotnet
|
||||||
|
environment:
|
||||||
|
- "Data:ConnectionString=Server=db;Port=4000;Database=votes;User=root;SslMode=None"
|
||||||
|
depends_on:
|
||||||
|
- message-queue
|
||||||
|
- db
|
||||||
|
|
||||||
|
message-queue:
|
||||||
|
image: nats:nanoserver
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: dockersamples/tidb:nanoserver
|
||||||
|
ports:
|
||||||
|
- "3306:4000"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
external:
|
||||||
|
name: nat
|
16
result/dotnet/Dockerfile
Normal file
16
result/dotnet/Dockerfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
FROM microsoft/dotnet:2.1-sdk as builder
|
||||||
|
|
||||||
|
WORKDIR /Result
|
||||||
|
COPY Result/Result.csproj .
|
||||||
|
RUN dotnet restore
|
||||||
|
|
||||||
|
COPY /Result .
|
||||||
|
RUN dotnet publish -c Release -o /out Result.csproj
|
||||||
|
|
||||||
|
# app image
|
||||||
|
FROM microsoft/dotnet:2.1-aspnetcore-runtime
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
ENTRYPOINT ["dotnet", "Result.dll"]
|
||||||
|
|
||||||
|
COPY --from=builder /out .
|
@ -1,14 +1,9 @@
|
|||||||
using System.Threading.Tasks;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
using Result.Models;
|
|
||||||
|
|
||||||
namespace Result.Hubs
|
namespace Result.Hubs
|
||||||
{
|
{
|
||||||
public class ResultsHub : Hub
|
public class ResultsHub : Hub
|
||||||
{
|
{
|
||||||
public async Task UpdateResults(ResultsModel results)
|
//no public methods, only used for push from PublishRTesultsTimer
|
||||||
{
|
|
||||||
await Clients.All.SendAsync("UpdateResults", results);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,5 @@
|
|||||||
public int OptionB { get; set; }
|
public int OptionB { get; set; }
|
||||||
|
|
||||||
public int VoteCount { get; set; }
|
public int VoteCount { get; set; }
|
||||||
|
|
||||||
public double OptionAPercent { get; set; }
|
|
||||||
|
|
||||||
public double OptionBPercent { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
@page
|
@page
|
||||||
|
@model Result.Pages.IndexModel
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Cats vs Dogs -- Result</title>
|
<title>@Model.OptionA vs @Model.OptionB -- Result</title>
|
||||||
<base href="/index.html">
|
<base href="/index.html">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale = 1.0">
|
<meta name="viewport" content="width=device-width, initial-scale = 1.0">
|
||||||
<meta name="keywords" content="docker-compose, docker, stack">
|
<meta name="keywords" content="docker-compose, docker, stack">
|
||||||
<meta name="author" content="Docker">
|
<meta name="author" content="Docker">
|
||||||
<link rel='stylesheet' href='/stylesheets/style.css' />
|
<link rel='stylesheet' href='~/css/site.css' />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="background-stats">
|
<div id="background-stats">
|
||||||
@ -22,22 +24,20 @@
|
|||||||
<div id="content-container">
|
<div id="content-container">
|
||||||
<div id="content-container-center">
|
<div id="content-container-center">
|
||||||
<div id="choice">
|
<div id="choice">
|
||||||
<div class="choice cats">
|
<div class="choice resulta">
|
||||||
<div class="label">Cats</div>
|
<div class="label">@Model.OptionA</div>
|
||||||
<div class="stat" id="optionA"></div>
|
<div class="stat" id="optionA"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="choice dogs">
|
<div class="choice resultb">
|
||||||
<div class="label">Dogs</div>
|
<div class="label">@Model.OptionB</div>
|
||||||
<div class="stat" id="optionB"></div>
|
<div class="stat" id="optionB"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="result">
|
<div id="result">
|
||||||
<span ng-if="total == 0">No votes yet</span>
|
<span id="totalVotes">No votes yet</span>
|
||||||
<span ng-if="total == 1">{{total}} vote</span>
|
|
||||||
<span ng-if="total >= 2">{{total}} votes</span>
|
|
||||||
</div>
|
</div>
|
||||||
<script src="~/lib/signalr/dist/browser/signalr.js"></script>
|
<script src="~/lib/signalr/dist/browser/signalr.js"></script>
|
||||||
<script src="~/js/results.js"></script>
|
<script src="~/js/results.js"></script>
|
||||||
|
@ -4,14 +4,30 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Result.Pages
|
namespace Result.Pages
|
||||||
{
|
{
|
||||||
public class IndexModel : PageModel
|
public class IndexModel : PageModel
|
||||||
{
|
{
|
||||||
|
private string _optionA;
|
||||||
|
private string _optionB;
|
||||||
|
protected readonly IConfiguration _configuration;
|
||||||
|
|
||||||
|
public string OptionA { get; private set; }
|
||||||
|
public string OptionB { get; private set; }
|
||||||
|
|
||||||
|
public IndexModel(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_optionA = _configuration.GetValue<string>("Voting:OptionA");
|
||||||
|
_optionB = _configuration.GetValue<string>("Voting:OptionB");
|
||||||
|
}
|
||||||
|
|
||||||
public void OnGet()
|
public void OnGet()
|
||||||
{
|
{
|
||||||
|
OptionA = _optionA;
|
||||||
|
OptionB = _optionB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"iisSettings": {
|
"iisSettings": {
|
||||||
"windowsAuthentication": false,
|
"windowsAuthentication": false,
|
||||||
"anonymousAuthentication": true,
|
"anonymousAuthentication": true,
|
||||||
"iisExpress": {
|
"iisExpress": {
|
||||||
"applicationUrl": "http://localhost:56785",
|
"applicationUrl": "http://localhost:56785",
|
||||||
"sslPort": 44369
|
"sslPort": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"profiles": {
|
"profiles": {
|
||||||
@ -18,10 +18,10 @@
|
|||||||
"Result": {
|
"Result": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
},
|
||||||
|
"applicationUrl": "https://localhost:5001;http://localhost:5000"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,15 +1,10 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.HttpsPolicy;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Result.Hubs;
|
using Result.Hubs;
|
||||||
|
using Result.Timers;
|
||||||
|
|
||||||
namespace Result
|
namespace Result
|
||||||
{
|
{
|
||||||
@ -26,6 +21,7 @@ namespace Result
|
|||||||
{
|
{
|
||||||
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
|
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
|
||||||
services.AddSignalR();
|
services.AddSignalR();
|
||||||
|
services.AddSingleton<PublishResultsTimer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||||
@ -45,6 +41,9 @@ namespace Result
|
|||||||
routes.MapHub<ResultsHub>("/resultsHub");
|
routes.MapHub<ResultsHub>("/resultsHub");
|
||||||
});
|
});
|
||||||
app.UseMvc();
|
app.UseMvc();
|
||||||
|
|
||||||
|
var timer = app.ApplicationServices.GetService<PublishResultsTimer>();
|
||||||
|
timer.Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
47
result/dotnet/Result/Timers/PublishResultsTimer.cs
Normal file
47
result/dotnet/Result/Timers/PublishResultsTimer.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Timers;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Result.Hubs;
|
||||||
|
using Result.Models;
|
||||||
|
|
||||||
|
namespace Result.Timers
|
||||||
|
{
|
||||||
|
public class PublishResultsTimer
|
||||||
|
{
|
||||||
|
private readonly IHubContext<ResultsHub> _hubContext;
|
||||||
|
private readonly Timer _timer;
|
||||||
|
//TODO- temp
|
||||||
|
private static Random _Random = new Random();
|
||||||
|
|
||||||
|
public PublishResultsTimer(IHubContext<ResultsHub> hubContext, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_hubContext = hubContext;
|
||||||
|
var publishMilliseconds = configuration.GetValue<int>("ResultsTimer:PublishMilliseconds");
|
||||||
|
_timer = new Timer(publishMilliseconds)
|
||||||
|
{
|
||||||
|
Enabled = false
|
||||||
|
};
|
||||||
|
_timer.Elapsed += PublishResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
if (!_timer.Enabled)
|
||||||
|
{
|
||||||
|
_timer.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PublishResults(object sender, ElapsedEventArgs e)
|
||||||
|
{
|
||||||
|
var model = new ResultsModel
|
||||||
|
{
|
||||||
|
OptionA = _Random.Next(0, 100),
|
||||||
|
OptionB = _Random.Next(0, 100)
|
||||||
|
};
|
||||||
|
model.VoteCount = model.OptionA + model.OptionB;
|
||||||
|
_hubContext.Clients.All.SendAsync("UpdateResults", model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,11 @@
|
|||||||
{
|
{
|
||||||
|
"Voting": {
|
||||||
|
"OptionA": "Cats",
|
||||||
|
"OptionB": "Dogs"
|
||||||
|
},
|
||||||
|
"ResultsTimer": {
|
||||||
|
"PublishMilliseconds": 1500
|
||||||
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Warning"
|
"Default": "Warning"
|
||||||
|
@ -95,12 +95,12 @@ body {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
#choice .choice.dogs {
|
#choice .choice.resultb {
|
||||||
color: #00cbca;
|
color: #00cbca;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
#choice .choice.cats {
|
#choice .choice.resulta {
|
||||||
color: #2196f3;
|
color: #2196f3;
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,23 @@
|
|||||||
var connection = new signalR.HubConnectionBuilder().withUrl("/resultsHub").build();
|
var connection = new signalR.HubConnectionBuilder().withUrl("/resultsHub").build();
|
||||||
|
|
||||||
connection.on("UpdateResults", function (results) {
|
connection.on("UpdateResults", function (results) {
|
||||||
data = JSON.parse(json);
|
document.body.style.opacity=1;
|
||||||
|
|
||||||
var a = parseInt(data.optionA || 0);
|
var a = parseInt(results.optionA || 0);
|
||||||
var b = parseInt(data.optionB || 0);
|
var b = parseInt(results.optionB || 0);
|
||||||
var percentages = getPercentages(a, b);
|
var percentages = getPercentages(a, b);
|
||||||
|
|
||||||
document.getElementById("optionA").innerText = percentages.a + "%";
|
document.getElementById("optionA").innerText = percentages.a + "%";
|
||||||
document.getElementById("optionB").innerText = percentages.b + "%";
|
document.getElementById("optionB").innerText = percentages.b + "%";
|
||||||
|
if (results.voteCount > 0) {
|
||||||
|
var totalVotes = results.voteCount + (results.voteCount > 1 ? " votes" : " vote");
|
||||||
|
document.getElementById("totalVotes").innerText = totalVotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bg1 = document.getElementById('background-stats-1');
|
||||||
|
var bg2 = document.getElementById('background-stats-2');
|
||||||
|
bg1.style.width = percentages.a + "%";
|
||||||
|
bg2.style.width = percentages.b + "%";
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.start().catch(function (err) {
|
connection.start().catch(function (err) {
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
|
||||||
// for details on configuring this project to bundle and minify static web assets.
|
|
||||||
|
|
||||||
// Write your Javascript code.
|
|
Loading…
Reference in New Issue
Block a user