diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4da0039 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +# Stage 1: Build the Angular application +FROM node:20 as build + +# Set the working directory +WORKDIR /app + +# Copy package.json and package-lock.json to install dependencies +COPY package*.json ./ + +# Install Node.js dependencies +RUN npm install + +# Copy the rest of the application code +COPY . . + +# Build the Angular application in production mode +RUN npm run build --configuration=prod + +# Stage 2: Serve the app with Nginx +FROM nginx:1.21-alpine + +# Copy the built app from the previous stage +COPY --from=build /app/dist/thanksgiving /usr/share/nginx/html + +# Copy the custom Nginx configuration +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Expose port 80 +EXPOSE 80 + +# Start Nginx server +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..fcd2c37 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,11 @@ +server { + listen 80; + server_name localhost; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 47a88fb..2653efe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "@angular/cli": "^16.2.9", "@angular/compiler-cli": "~16.2.12", "@types/jasmine": "~3.8.0", - "@types/node": "^12.11.1", + "@types/node": "^12.20.55", "jasmine-core": "~3.8.0", "karma": "~6.3.0", "karma-chrome-launcher": "~3.1.0", @@ -3646,7 +3646,8 @@ "version": "12.20.55", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/node-forge": { "version": "1.3.9", diff --git a/package.json b/package.json index 808906e..d06d966 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "@angular/cli": "^16.2.9", "@angular/compiler-cli": "~16.2.12", "@types/jasmine": "~3.8.0", - "@types/node": "^12.11.1", + "@types/node": "^12.20.55", "jasmine-core": "~3.8.0", "karma": "~6.3.0", "karma-chrome-launcher": "~3.1.0", diff --git a/src/app.constants.ts b/src/app.constants.ts deleted file mode 100644 index c177038..0000000 --- a/src/app.constants.ts +++ /dev/null @@ -1,4 +0,0 @@ - export const API_URL = 'http://127.0.0.1:3000'; -export const WEBSOCK_URL = 'http://127.0.0.1:3000'; -// export const API_URL = 'https://thanksgiving2023.ngweb.io/api'; - //export const WEBSOCK_URL = "https://thanksgiving2023.ngweb.io/" diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 07d46db..d5b8318 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,6 +1,5 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { io, Socket } from "socket.io-client"; -import { API_URL, WEBSOCK_URL } from '../app.constants'; import { EventService } from "./services/event.service"; import {EventStateChanged, ServerEvent, VersusBeginEvent} from "../types/server-event"; import { ApiService } from "./services/api.service"; @@ -11,6 +10,7 @@ import { VoiceService } from "./services/voice.service"; import { Subject } from "rxjs"; import { getAudioPath } from "./helper/tts.helper"; import {animate, keyframes, style, transition, trigger} from "@angular/animations"; +import {environment} from "../environments/environment"; @Component({ selector: 'app-root', @@ -32,7 +32,7 @@ import {animate, keyframes, style, transition, trigger} from "@angular/animation }) export class AppComponent implements OnInit, OnDestroy { title = 'thanksgiving'; - connection = io(WEBSOCK_URL, { transports: ['websocket']}); + connection = io(environment.WEBSOCK_URL, { transports: ['websocket']}); destroyed = new Subject(); versusData: VersusBeginEvent|null = null; audioSrc: string; @@ -55,6 +55,7 @@ export class AppComponent implements OnInit, OnDestroy { }); this.apiService.getAppState('main').subscribe((result) => { if(this.router.url.indexOf('admin') === -1) { + console.log(this.router.url); this.router.navigate([`/${result.value}`]).then(() => { console.log(`navigated to ${result.value}`); }) diff --git a/src/app/components/card-played/card-played.component.ts b/src/app/components/card-played/card-played.component.ts index c3de195..2291bb2 100644 --- a/src/app/components/card-played/card-played.component.ts +++ b/src/app/components/card-played/card-played.component.ts @@ -1,12 +1,11 @@ import { Component, OnInit } from '@angular/core'; import { EventService } from "../../services/event.service"; import { filter, map } from "rxjs/operators"; -import { EventCardPlayed } from "../../../types/server-event"; import { ApiService } from "../../services/api.service"; import { animate, style, transition, trigger } from "@angular/animations"; -import { API_URL } from "../../../app.constants"; import { getAudioPath } from "../../helper/tts.helper"; import { VoiceService } from "../../services/voice.service"; +import {environment} from "../../../environments/environment"; @Component({ selector: 'app-card-played', @@ -59,7 +58,7 @@ export class CardPlayedComponent implements OnInit { }) } getImageUrl() { - return `${API_URL}/guests/photo/${this.participantId}?$t=${this.imgTimestamp}`; + return `${environment.API_URL}/guests/photo/${this.participantId}?$t=${this.imgTimestamp}`; } getAudioSrc(text: string) { diff --git a/src/app/components/game-pause/game-pause.component.ts b/src/app/components/game-pause/game-pause.component.ts index f772170..663f371 100644 --- a/src/app/components/game-pause/game-pause.component.ts +++ b/src/app/components/game-pause/game-pause.component.ts @@ -9,7 +9,7 @@ import { getAudioPath } from "../../helper/tts.helper"; }) export class GamePauseComponent implements OnInit, OnDestroy { tstamp = new Date().getTime(); - private interval: number; + private interval: NodeJS.Timeout; constructor(private voiceService: VoiceService) { } diff --git a/src/app/components/participant-item/participant-item.component.ts b/src/app/components/participant-item/participant-item.component.ts index 6716f02..235716f 100644 --- a/src/app/components/participant-item/participant-item.component.ts +++ b/src/app/components/participant-item/participant-item.component.ts @@ -4,9 +4,9 @@ import { EventService } from "../../services/event.service"; import { Observable, Subject, Subscription } from "rxjs"; import { filter, map, takeUntil } from "rxjs/operators"; import { EventCardPlayed, EventCardsChanged, EventPhotosUpdated, ServerEvent } from "../../../types/server-event"; -import { API_URL } from "../../../app.constants"; import { ApiService } from "../../services/api.service"; import { CardItem } from "../../../types/card-item"; +import {environment} from "../../../environments/environment"; @Component({ selector: 'app-participant-item', @@ -74,7 +74,7 @@ export class ParticipantItemComponent implements OnInit, OnDestroy, OnChanges { getImageUrl() { if(this.participant) { - return `${API_URL}/guests/photo/${this.participant.telegramId}?$t=${this.imgTimestamp}`; + return `${environment.API_URL}/guests/photo/${this.participant.telegramId}?$t=${this.imgTimestamp}`; } return null; } diff --git a/src/app/helper/tts.helper.ts b/src/app/helper/tts.helper.ts index f6f0090..0cd8eee 100644 --- a/src/app/helper/tts.helper.ts +++ b/src/app/helper/tts.helper.ts @@ -1,9 +1,10 @@ -import { API_URL } from "../../app.constants"; +import {environment} from "../../environments/environment"; + export function getAudioPath(text: string, voice: number = 1) { - return `${API_URL}/voice/tts?text=${text}&voice=${voice}`; + return `${environment.API_URL}/voice/tts?text=${text}&voice=${voice}`; } export function getAudioPathWithTemplate(path: string, text: string, vars: { [index: string]: string }) { const t = new Date().getTime(); - return `${API_URL}/voice/${path}?text=${text}&vars=${JSON.stringify(vars)}&t=${t}`; + return `${environment.API_URL}/voice/${path}?text=${text}&vars=${JSON.stringify(vars)}&t=${t}`; } diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 4875104..c7886d6 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; import { HttpClient } from "@angular/common/http"; import { Observable } from "rxjs"; -import { API_URL } from "../../app.constants"; import { AppState } from "../../types/app-state"; import { Participant } from "../../types/participant"; import { Question } from "../../types/question"; @@ -12,6 +11,7 @@ import { PrizeDto } from "../../types/prize.dto"; import {QuestionresultsDto} from "../../types/questionresults.dto"; import {map} from "rxjs/operators"; import {VersusItem} from "../../types/versus-item"; +import {environment} from "../../environments/environment"; export class FeatureFlagStateDto { name: string; @@ -57,83 +57,85 @@ export interface EndgameResultsDto { }) export class ApiService { - constructor(private httpClient: HttpClient) { } + constructor(private httpClient: HttpClient) { + console.log(environment.API_URL); + } public getAppState(state: string): Observable { - return this.httpClient.get(`${API_URL}/state/${state}`); + return this.httpClient.get(`${environment.API_URL}/state/${state}`); } public getParticipants(): Observable { - return this.httpClient.get(`${API_URL}/guests`); + return this.httpClient.get(`${environment.API_URL}/guests`); } public getParticipant(id: number): Observable { - return this.httpClient.get(`${API_URL}/guests/${id}`); + return this.httpClient.get(`${environment.API_URL}/guests/${id}`); } public getQuestion(): Observable { - return this.httpClient.get(`${API_URL}/quiz`); + return this.httpClient.get(`${environment.API_URL}/quiz`); } public setAppState(state: string, value: string) { - return this.httpClient.post(`${API_URL}/state`, { + return this.httpClient.post(`${environment.API_URL}/state`, { state, value }); } getCards(telegramId: number): Observable { - return this.httpClient.get(`${API_URL}/cards/${telegramId}`); + return this.httpClient.get(`${environment.API_URL}/cards/${telegramId}`); } continueGame() { console.log(`continue game`); - return this.httpClient.post(`${API_URL}/quiz/proceed`, {}); + return this.httpClient.post(`${environment.API_URL}/quiz/proceed`, {}); } markQueueAsCompleted(_id: string) { - return this.httpClient.post(`${API_URL}/game/${_id}/complete`, {}); + return this.httpClient.post(`${environment.API_URL}/game/${_id}/complete`, {}); } pauseGame() { - return this.httpClient.post(`${API_URL}/game/pause`, {}); + return this.httpClient.post(`${environment.API_URL}/game/pause`, {}); } resumeGame() { - return this.httpClient.post(`${API_URL}/game/resume`, {}); + return this.httpClient.post(`${environment.API_URL}/game/resume`, {}); } getGameState() { - return this.httpClient.get(`${API_URL}/game/state`); + return this.httpClient.get(`${environment.API_URL}/game/state`); } getPenalty() { console.log(`get penalty`); - return this.httpClient.get(`${API_URL}/penalty`); + return this.httpClient.get(`${environment.API_URL}/penalty`); } playExtraCards() { console.log(`play extra cards`); - return this.httpClient.get(`${API_URL}/game/playextracards`); + return this.httpClient.get(`${environment.API_URL}/game/playextracards`); } getAdditionalQuestion(target: number) { - return this.httpClient.post(`${API_URL}/quiz/extraquestion`, { + return this.httpClient.post(`${environment.API_URL}/quiz/extraquestion`, { telegramId: target, }); } getImageUrl(id: number) { const timestamp = new Date().getTime(); - return `${API_URL}/guests/photo/${id}?$t=${timestamp}}`; + return `${environment.API_URL}/guests/photo/${id}?$t=${timestamp}}`; } getPrize(): Observable { - return this.httpClient.get(`${API_URL}/gifts`); + return this.httpClient.get(`${environment.API_URL}/gifts`); } getQuestionResults() { - return this.httpClient.get(`${API_URL}/quiz/question-results`).pipe(map((data) => + return this.httpClient.get(`${environment.API_URL}/quiz/question-results`).pipe(map((data) => data.map((item) => { return { ...item, @@ -144,29 +146,29 @@ export class ApiService { } getFeatureFlagState(feature: string) { - return this.httpClient.get(`${API_URL}/featureflag/${feature}`); + return this.httpClient.get(`${environment.API_URL}/featureflag/${feature}`); } setFeatureFlagState(feature: string, state: boolean) { - return this.httpClient.post(`${API_URL}/featureflag`, { name: feature, state: state }); + return this.httpClient.post(`${environment.API_URL}/featureflag`, { name: feature, state: state }); } getStateDetails() { - return this.httpClient.get(`${API_URL}/game/state-details`); + return this.httpClient.get(`${environment.API_URL}/game/state-details`); } getVersus() { - return this.httpClient.get(`${API_URL}/versus`); + return this.httpClient.get(`${environment.API_URL}/versus`); } completeVersus(winner: number, loser: number) { - return this.httpClient.post(`${API_URL}/versus/complete`, { + return this.httpClient.post(`${environment.API_URL}/versus/complete`, { winner: winner, loser: loser }); } getEndgameResults() { - return this.httpClient.get(`${API_URL}/quiz/endgame-results`) + return this.httpClient.get(`${environment.API_URL}/quiz/endgame-results`) } } diff --git a/src/app/services/testing-api.service.ts b/src/app/services/testing-api.service.ts index 00290f5..a5a16c0 100644 --- a/src/app/services/testing-api.service.ts +++ b/src/app/services/testing-api.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import {HttpClient} from "@angular/common/http"; -import {API_URL} from "../../app.constants"; +import {environment} from "../../environments/environment"; @Injectable({ providedIn: 'root' @@ -10,26 +10,26 @@ export class TestingApiService { constructor(private httpClient: HttpClient) { } public simulateVersus() { - return this.httpClient.post(`${API_URL}/versus/simulate-versus`, {}); + return this.httpClient.post(`${environment.API_URL}/versus/simulate-versus`, {}); } resetAllVersusTasksAsIncompleted() { - return this.httpClient.post(`${API_URL}/versus/reset-all`, {}); + return this.httpClient.post(`${environment.API_URL}/versus/reset-all`, {}); } resetAllPlayersScore() { - return this.httpClient.post(`${API_URL}/guests/reset-score`, {}); + return this.httpClient.post(`${environment.API_URL}/guests/reset-score`, {}); } clearGameQueue() { - return this.httpClient.post(`${API_URL}/game/clear-queue`, {}); + return this.httpClient.post(`${environment.API_URL}/game/clear-queue`, {}); } simulateEndGamePoints() { - return this.httpClient.post(`${API_URL}/quiz/calculate-endgame-extrapoints`, {}) + return this.httpClient.post(`${environment.API_URL}/quiz/calculate-endgame-extrapoints`, {}) } simulateValidAnswer() { - return this.httpClient.post(`${API_URL}/game/simulate-valid-answer`, {}); + return this.httpClient.post(`${environment.API_URL}/game/simulate-valid-answer`, {}); } } diff --git a/src/app/services/voice.service.ts b/src/app/services/voice.service.ts index 2f7cf13..253061d 100644 --- a/src/app/services/voice.service.ts +++ b/src/app/services/voice.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders, HttpParams } from "@angular/common/http"; -import { API_URL } from "../../app.constants"; import {delay, delayWhen, interval, Observable, of, Subject} from "rxjs"; import {ApiService} from "./api.service"; import {takeUntil, tap} from "rxjs/operators"; import {SharedMethods} from "../shared/sharedmethods"; +import {environment} from "../../environments/environment"; @Injectable({ providedIn: 'root' @@ -45,11 +45,11 @@ export class VoiceService { } getAudioUrl(text: string,voice: number = 1) { - return `${API_URL}/voice/tts?voice=${voice}&text=${text}` + return `${environment.API_URL}/voice/tts?voice=${voice}&text=${text}` } getAudioUrlSSML(text: string) { - return `${API_URL}/voice/ssml?text=${encodeURI(text)}` + return `${environment.API_URL}/voice/ssml?text=${encodeURI(text)}` } audioEnded() { diff --git a/src/app/views/onboarding/onboarding.component.html b/src/app/views/onboarding/onboarding.component.html index 11b6408..8b1cab9 100644 --- a/src/app/views/onboarding/onboarding.component.html +++ b/src/app/views/onboarding/onboarding.component.html @@ -22,11 +22,11 @@

Если тебе требуется пояснять эту карточку - то игра не для тебя, налей себе алкоголь и побольше.

-
- ... +
+ ...
-
Говнокарта
-

Можно подкинуть еще один вопрос игроку, который правильно ответил.

+
Поединок
+

Можно вызвать другого игрока на схватку 1 на 1 в мини-игре

diff --git a/src/app/views/onboarding/onboarding.component.ts b/src/app/views/onboarding/onboarding.component.ts index 1c397d8..cc787b6 100644 --- a/src/app/views/onboarding/onboarding.component.ts +++ b/src/app/views/onboarding/onboarding.component.ts @@ -33,7 +33,7 @@ interface RuleItem { export class OnboardingComponent implements OnInit, OnDestroy { @ViewChild('avoidPenaltyCard') private avoidPenaltyCardEl: ElementRef; @ViewChild('stolePrizeCard') private stolePrizeCardEl: ElementRef; - @ViewChild('shitCard') private shitCardEl: ElementRef; + @ViewChild('versusCard') private versusCardEl: ElementRef; @ViewChild('luckyCard') private luckyCardEl: ElementRef; @ViewChild('banPlayerCard') private banPlayerEl: ElementRef; @ViewChild('doubleTreasureCard') private doubleTreasureCardEl: ElementRef; @@ -42,24 +42,19 @@ export class OnboardingComponent implements OnInit, OnDestroy { { text: 'Вопросы и ответы будут отображаться на экране и в Боте Благодарения.' }, { text: 'Каждый игрок в начале игры имеет на руках 4 карты, набор карт определяется случайно. Описание карт ты найдешь ниже. После использования карты ты получаешь новую случайную карту.' }, { text: 'На разыгрывание карты время ограничено, примерно 10 секунд.' }, - { text: 'Задача игрока - ответить правильно и быстрее других.' }, - { text: 'Первый игрок, ответивший правильно, получает одно очко и шанс выиграть приз.' }, - { text: 'Приз??? Какой приз?', screpa: true, voice: 2 }, - { text: 'Я не думаю, что их мозгов хватит для получения призов', screpa: true, voice: 2 }, - { text: 'А ты вообще кто такая?', hideWithoutVoice: true }, - { text: 'Ах, да.. простите, забыла представиться, я - Скрепа по фамилии Духовная', screpa: true, voice: 2}, - { text: 'И на кой ты нам нужна?', hideWithoutVoice: true }, - { text: 'Я тут, чтобы нарушать ход игры, и вообще тебя не спрашивали. ', screpa: true, voice: 2}, - { text: '[Ладно, ]В общем - чем больше правильных ответов - тем больше призов '}, - { text: 'Первый игрок, ответивший неправильно, получает наказание, и мы переходим к следующему вопросу' }, + { text: 'Вы долго просили оптимизировать геймплей для медленных и глупых, и мы это сделали!'}, + { text: 'Задача игрока - ответить правильно и быстрее других, ну или хотя бы просто правильно в течение 20 секунд' }, + { text: 'Первый игрок, ответивший правильно, получает два очка' }, + { text: 'Все остальные, ответившие правильно, получают одно очко'}, + { text: 'Иногда за неправильные ответы игроки будут получать наказания' }, { text: 'Избежать наказания можно только с помощью соотвествуещей карты, данную карту ты можешь сыграть перед озвучиванием наказания', action: () => { this.shakeCard(this.avoidPenaltyCardEl); }}, { text: 'Карту "украсть приз" ты можешь сыграть в момент, когда кто-то собирается получить награду, но до момента того, как ты узнаешь, что это именно за приз', action: () => { this.shakeCard(this.stolePrizeCardEl); }}, - { text: '"Говно-карту" ты можешь разыграть в момент, когда кто-то ответил правильно, тем самым ты заставишь именно этого игрока ответить на один дополнительный вопрос. На одного игрока можно сыграть неограниченное количество этих карт', action: () => { - this.shakeCard(this.shitCardEl); + { text: 'Карту "Поединок" ты можешь разыграть в любой момент, чтобы вызвать игрока на дуэль', action: () => { + this.shakeCard(this.versusCardEl); }}, { text: '"Лаки карту" ты сможешь сыграть после своего правильного ответа, она увеличит твои шансы на получение приза', action: () => { this.shakeCard(this.luckyCardEl); @@ -78,22 +73,10 @@ export class OnboardingComponent implements OnInit, OnDestroy { }, { text: 'Не торопись с ответами, игра идет до той поры, пока мы не разыграем все призы' }, { - text: 'Ах да, так как создатель игры, работает на Microsoft, мы добавили привычные вам баги, сейчас расскажу', - screpa: true, - voice: 2 + text: 'Ах да, так как создатель игры, работает на Microsoft, мы добавили привычные вам баги, но все их оставим в секрете', }, { - text: 'Если у вас нет вариантов ответа - перезайдите в бот или перезапустите телеграмм', - screpa: true, - voice: 2, - }, - { - text: 'Остальные баги будут сюрпризом! Но не забывайте громко кричать, когда что-то работает не так', - screpa: true, - voice: 2, - }, - { - text: 'Кажется правила закончились' + text: 'Кажется, правила закончились' } ]; @@ -172,7 +155,7 @@ export class OnboardingComponent implements OnInit, OnDestroy { this.allRulesAnnounced = true; this.voiceService.playAudio(getAudioPath(`Это все правила, надеюсь, все понятно. А если нет - сейчас Кирилл и Оксана вам все пояснят, ну и совсем для тупых - пустила по кругу правила на экране, - а если ты их не поймешь - то за Путина голосовать пойдешь (или за Грузинскую мечту) . Каждый правильный ответ отнимает у Путина год жизни, постарайтесь!`)); + а если ты их не поймешь - то очень жаль тебя глупенького`)); this.voiceService.audioEndedSubject.pipe(takeUntil(this.destroyed$),take(1)).subscribe(() => { setInterval(() => { this.playNextRule() }, 6000); this.currentRulePosition = 0 diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 3612073..b8d4a3e 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,3 +1,5 @@ export const environment = { - production: true + production: true, + API_URL: "https://thanksgiving2024.ngweb.io/api", + WEBSOCK_URL: "https://thanksgiving2024.ngweb.io" }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index f56ff47..3062ec4 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -3,7 +3,9 @@ // The list of file replacements can be found in `angular.json`. export const environment = { - production: false + production: false, + API_URL: "http://localhost:3000", + WEBSOCK_URL: "http://localhost:3000" }; /* diff --git a/tsconfig.app.json b/tsconfig.app.json index 82d91dc..b120c1d 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -3,7 +3,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/app", - "types": [] + "types": ["node"] }, "files": [ "src/main.ts",