From 760a26e51a41e52c942a529708aa3443f89383d8 Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Tue, 8 May 2018 15:35:23 +0100 Subject: [PATCH] Docker changes --- client/src/app/actions/chat.actions.ts | 41 +++++++++++++++ client/src/app/effects/chat.effects.ts | 38 ++++++++++++++ client/src/app/models/chat.model.ts | 3 ++ client/src/app/reducers/chat.reducer.ts | 50 +++++++++++++++++++ client/src/app/services/chat.service.ts | 24 +++++++++ server/Controllers/ChatController.cs | 29 +++++++++++ server/Dockerfile | 34 +++++++------ .../{Dockerfile.alpine => Dockerfile.jessie} | 32 +++++------- server/Models/ViewModels/ChatViewModel.cs | 5 ++ server/Services/Hubs/ChatHub.cs | 21 ++++++++ 10 files changed, 244 insertions(+), 33 deletions(-) create mode 100644 client/src/app/actions/chat.actions.ts create mode 100644 client/src/app/effects/chat.effects.ts create mode 100644 client/src/app/models/chat.model.ts create mode 100644 client/src/app/reducers/chat.reducer.ts create mode 100644 client/src/app/services/chat.service.ts create mode 100644 server/Controllers/ChatController.cs rename server/{Dockerfile.alpine => Dockerfile.jessie} (55%) create mode 100644 server/Models/ViewModels/ChatViewModel.cs create mode 100644 server/Services/Hubs/ChatHub.cs diff --git a/client/src/app/actions/chat.actions.ts b/client/src/app/actions/chat.actions.ts new file mode 100644 index 0000000..92b0a3f --- /dev/null +++ b/client/src/app/actions/chat.actions.ts @@ -0,0 +1,41 @@ +import { ProfileModel } from './../models/profile.model'; +import { Action } from '@ngrx/store'; +import { ChatModel } from '../models/chat.model'; +//#region Load +export const LOAD = '[Chat] Load'; +export const LOAD_SUCCESS = '[Chat] Load Success'; +export const LOAD_FAIL = '[Chat] Load Fail'; +export class LoadAction implements Action { + readonly type = LOAD; + constructor() {} +} +export class LoadSuccessAction implements Action { + readonly type = LOAD_SUCCESS; + constructor(public payload: ChatModel[]) {} +} +export class LoadFailAction implements Action { + readonly type = LOAD_FAIL; + constructor(public payload: any) {} +} +//#region Add +export const ADD = '[Chat] Add Chat'; +export const ADD_SUCCESS = '[Chat] Add Chat Success'; +export const ADD_FAIL = '[Chat] Add Chat Fail'; +export class AddAction implements Action { + readonly type = ADD; + constructor(public payload: ChatModel) { + } +} +export class AddSuccessAction implements Action { + readonly type = ADD_SUCCESS; + constructor(public payload: ChatModel) { + } +} +//#endregion +export type Actions = + | LoadAction + | LoadSuccessAction + | LoadFailAction + | AddAction + | AddSuccessAction; + diff --git a/client/src/app/effects/chat.effects.ts b/client/src/app/effects/chat.effects.ts new file mode 100644 index 0000000..ceef335 --- /dev/null +++ b/client/src/app/effects/chat.effects.ts @@ -0,0 +1,38 @@ +import { + LoadAction, + LOAD_FAIL +} from './../actions/chat.actions'; +import { Injectable } from '@angular/core'; +import { Actions, Effect } from '@ngrx/effects'; +import { Observable } from 'rxjs/Observable'; +import { ProfileService } from 'app/services/profile.service'; + +import * as chat from '../actions/chat.actions'; + +import 'rxjs/add/operator/switchMap'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/catch'; +import { ChatService } from '../services/chat.service'; + +@Injectable() +export class ChatEffects { + @Effect() + get$ = this.actions$ + .ofType(chat.LOAD) + .switchMap((payload: chat.LoadAction) => + this._service + .get() + .map(res => ({ type: chat.LOAD_SUCCESS, payload: res })) + .catch(err => { + console.error('ChatEffects', 'get$', err); + return Observable.of({ type: chat.LOAD_FAIL }); + }) + ); + @Effect() + put$ = this.actions$ + .ofType(chat.ADD) + .switchMap((action: chat.AddAction) => this._service.send(action.payload)) + .map(res => ({ type: chat.ADD_SUCCESS, payload: res })); + + constructor(private _service: ChatService, private actions$: Actions) {} +} diff --git a/client/src/app/models/chat.model.ts b/client/src/app/models/chat.model.ts new file mode 100644 index 0000000..c79b78f --- /dev/null +++ b/client/src/app/models/chat.model.ts @@ -0,0 +1,3 @@ +export class ChatModel { + message: string; +} diff --git a/client/src/app/reducers/chat.reducer.ts b/client/src/app/reducers/chat.reducer.ts new file mode 100644 index 0000000..8e29c5b --- /dev/null +++ b/client/src/app/reducers/chat.reducer.ts @@ -0,0 +1,50 @@ +import * as chat from '../actions/chat.actions'; +import * as _ from 'lodash'; +import { ChatModel } from '../models/chat.model'; + +export interface State { + loading: boolean; + result: ChatModel[]; +} + +export const initialState: State = { + loading: false, + result: [] +}; + +export function reducer(state = initialState, action: chat.Actions): State { + switch (action.type) { + case chat.LOAD: { + return { + ...state, + loading: true + }; + } + case chat.LOAD_SUCCESS: { + return { + ...state, + result: action.payload, + loading: false + }; + } + case chat.LOAD_FAIL: { + return { + ...state, + loading: false + }; + } + case chat.ADD_SUCCESS: { + const newResults = _.clone(state.result); + newResults.push(action.payload); + const newState = { + ...state, + result: newResults, + loading: false + }; + return newState; + } + default: { + return state; + } + } +} diff --git a/client/src/app/services/chat.service.ts b/client/src/app/services/chat.service.ts new file mode 100644 index 0000000..c85cd6e --- /dev/null +++ b/client/src/app/services/chat.service.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { BaseService } from './base.service'; +import { Observable } from 'rxjs/Observable'; +import { environment } from 'environments/environment'; +import { ChatModel } from 'app/models/chat.model'; +import { HttpClient } from '@angular/common/http'; + +@Injectable() +export class ChatService extends BaseService { + constructor(private _http: HttpClient) { + super(); + } + + get(): Observable { + return this._http.get(environment.API_HOST + '/chat/'); + } + + send(item: ChatModel): Observable { + return this._http.post( + environment.API_HOST + '/chat/', + item + ); + } +} diff --git a/server/Controllers/ChatController.cs b/server/Controllers/ChatController.cs new file mode 100644 index 0000000..a9d4cf2 --- /dev/null +++ b/server/Controllers/ChatController.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.SignalR; +using PodNoms.Api.Models.ViewModels; +using PodNoms.Api.Services.Auth; +using PodNoms.Api.Services.Hubs; + +namespace PodNoms.Api.Controllers { + [Route("[controller]")] + [Authorize] + public class ChatController : BaseAuthController { + private readonly HubLifetimeManager _hub; + + public ChatController(IHttpContextAccessor contextAccessor, UserManager userManager, + HubLifetimeManager chatHubContext) : + base(contextAccessor, userManager) { + this._hub = chatHubContext; + } + + [HttpPost] + public async Task> Post([FromBody]ChatViewModel message) { + await this._hub.SendAllAsync("SendMessage", new object[] { message.Message }); + return Ok(message); + } + } +} diff --git a/server/Dockerfile b/server/Dockerfile index 486d0a4..b03a2ba 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,29 +1,35 @@ -FROM microsoft/dotnet:2.1-sdk as build-env -# FROM microsoft/aspnetcore-build:2.1.300-preview2-stretch AS build-env +FROM microsoft/dotnet:2.1-sdk-alpine AS build WORKDIR /app -# Copy csproj and restore as distinct layers -COPY *.csproj ./ -COPY NuGet.config ./ +# copy csproj and restore as distinct layers +COPY *.csproj . RUN dotnet restore \ --source https://api.nuget.org/v3/index.json \ --source https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json \ --source https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json \ - --source https://www.myget.org/F/sixlabors/api/v3/index.json + --source https://www.myget.org/F/sixlabors/api/v3/index.json -# Copy everything else and build -COPY . ./ +# copy everything else and build app +COPY . . +WORKDIR /app/ +RUN dotnet build + +FROM build AS publish +WORKDIR /app RUN dotnet publish -c Release -o out -# Build runtime image -FROM microsoft/dotnet:2.1-aspnetcore-runtime -WORKDIR /app -COPY --from=build-env /app/out . -RUN apt-get update && apt-get install -y \ +FROM microsoft/dotnet:2.1-aspnetcore-runtime-alpine AS runtime +WORKDIR /app +COPY --from=publish /app/out ./ +# ln -s /usr/lib/libuv.so.1 /usr/lib/libuv.so && \ + +RUN apk add --no-cache --update \ python \ ffmpeg \ - curl && \ + libuv \ + curl \ + curl-dev && \ curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl && \ chmod a+rx /usr/local/bin/youtube-dl && \ youtube-dl -U diff --git a/server/Dockerfile.alpine b/server/Dockerfile.jessie similarity index 55% rename from server/Dockerfile.alpine rename to server/Dockerfile.jessie index 01e123c..486d0a4 100644 --- a/server/Dockerfile.alpine +++ b/server/Dockerfile.jessie @@ -1,35 +1,29 @@ -FROM microsoft/dotnet:2.1.300-preview2-sdk-alpine AS build +FROM microsoft/dotnet:2.1-sdk as build-env +# FROM microsoft/aspnetcore-build:2.1.300-preview2-stretch AS build-env WORKDIR /app -# copy csproj and restore as distinct layers -COPY *.csproj . +# Copy csproj and restore as distinct layers +COPY *.csproj ./ +COPY NuGet.config ./ RUN dotnet restore \ --source https://api.nuget.org/v3/index.json \ --source https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json \ --source https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json \ - --source https://www.myget.org/F/sixlabors/api/v3/index.json + --source https://www.myget.org/F/sixlabors/api/v3/index.json -# copy everything else and build app -COPY . . -WORKDIR /app/ -RUN dotnet build - -FROM build AS publish -WORKDIR /app +# Copy everything else and build +COPY . ./ RUN dotnet publish -c Release -o out - -FROM microsoft/dotnet:2.1.0-preview2-aspnetcore-runtime-alpine AS runtime +# Build runtime image +FROM microsoft/dotnet:2.1-aspnetcore-runtime WORKDIR /app -COPY --from=publish /app/out ./ -# ln -s /usr/lib/libuv.so.1 /usr/lib/libuv.so && \ +COPY --from=build-env /app/out . -RUN apk add --no-cache --update \ +RUN apt-get update && apt-get install -y \ python \ ffmpeg \ - libuv \ - curl \ - curl-dev && \ + curl && \ curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl && \ chmod a+rx /usr/local/bin/youtube-dl && \ youtube-dl -U diff --git a/server/Models/ViewModels/ChatViewModel.cs b/server/Models/ViewModels/ChatViewModel.cs new file mode 100644 index 0000000..35722d1 --- /dev/null +++ b/server/Models/ViewModels/ChatViewModel.cs @@ -0,0 +1,5 @@ +namespace PodNoms.Api.Models.ViewModels { + public class ChatViewModel { + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/server/Services/Hubs/ChatHub.cs b/server/Services/Hubs/ChatHub.cs new file mode 100644 index 0000000..089a2eb --- /dev/null +++ b/server/Services/Hubs/ChatHub.cs @@ -0,0 +1,21 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.SignalR; + +namespace PodNoms.Api.Services.Hubs { + [Authorize] + public class ChatHub : Hub { + public override async Task OnConnectedAsync() { + await Clients.All.SendAsync("SendAction", Context.User.Identity.Name, "joined"); + } + + public override async Task OnDisconnectedAsync(Exception ex) { + await Clients.All.SendAsync("SendAction", Context.User.Identity.Name, "left"); + } + + public async Task Send(string message) { + await Clients.All.SendAsync("SendMessage", Context.User.Identity.Name, message); + } + } +} \ No newline at end of file