Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/corba-cd.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ jobs:
run: |
# -f apunta al Dockerfile un nivel arriba
# El '.' final se cambia por '..' para que el contexto sea ./corba-services
docker build -f Dockerfile.prod -t us-east1-docker.pkg.dev/cnsa-2026/draftkings/corba-news-manager:0.0.1 -f ../Dockerfile ..
docker build -f Dockerfile.prod -t us-east1-docker.pkg.dev/cnsa-2026/draftkings/corba-news-manager:1.0.0 -f ../Dockerfile ..

- name: Push Docker image to Artifact Registry
run: |
docker push us-east1-docker.pkg.dev/cnsa-2026/draftkings/corba-news-manager:0.0.1
docker push us-east1-docker.pkg.dev/cnsa-2026/draftkings/corba-news-manager:1.0.0

# 1. Instalar la llave ANTES de comprobar la conexión
- name: Install SSH Key
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ionic-ci-cd.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -357,18 +357,18 @@ jobs:
# Que la imagen se suba con el tag actual cuando se etnga el actions del tag
- name: Build Docker image
run: |
docker build -t us-east1-docker.pkg.dev/cnsa-2026/draftkings/client-ionic:0.0.1 .
docker build -t us-east1-docker.pkg.dev/cnsa-2026/draftkings/client-ionic:1.0.0 .

- name: Push Docker image to Artifact Registry
run: |
docker push us-east1-docker.pkg.dev/cnsa-2026/draftkings/client-ionic:0.0.1
docker push us-east1-docker.pkg.dev/cnsa-2026/draftkings/client-ionic:1.0.0

- name: Deploy to Cloud Run
uses: google-github-actions/deploy-cloudrun@v3
with:
service: "draftkings"
region: "us-east1"
image: "us-east1-docker.pkg.dev/cnsa-2026/draftkings/client-ionic:0.0.1"
image: "us-east1-docker.pkg.dev/cnsa-2026/draftkings/client-ionic:1.0.0"
flags: "--service-account=draftkings@cnsa-2026.iam.gserviceaccount.com --allow-unauthenticated --ingress=all"
secrets: |
FIREBASE_CONFIG_PROD=FIREBASE_CONFIG_PROD:latest
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/node-cd.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ jobs:

- name: Build Docker image
run: |
docker build -f Dockerfile.prod -t us-east1-docker.pkg.dev/cnsa-2026/draftkings/api-node:0.0.1 .
docker build -f Dockerfile.prod -t us-east1-docker.pkg.dev/cnsa-2026/draftkings/api-node:1.0.0 .

- name: Push Docker image to Artifact Registry
run: |
docker push us-east1-docker.pkg.dev/cnsa-2026/draftkings/api-node:0.0.1
docker push us-east1-docker.pkg.dev/cnsa-2026/draftkings/api-node:1.0.0

- name: Deploy to Cloud Run
uses: google-github-actions/deploy-cloudrun@v3
with:
service: "dk-node"
region: "us-east1"
image: "us-east1-docker.pkg.dev/cnsa-2026/draftkings/api-node:0.0.1"
image: "us-east1-docker.pkg.dev/cnsa-2026/draftkings/api-node:1.0.0"
flags: "--service-account=draftkings@cnsa-2026.iam.gserviceaccount.com --allow-unauthenticated --ingress=all --set-secrets=/app/enviroments/env=NODE_ENV_PROD:latest"
6 changes: 3 additions & 3 deletions .github/workflows/spring-cd.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ jobs:

- name: Build Docker image
run: |
docker build -f Dockerfile.prod -t us-east1-docker.pkg.dev/cnsa-2026/draftkings/${{ steps.slug.outputs.service_slug }}:0.0.1 .
docker build -f Dockerfile.prod -t us-east1-docker.pkg.dev/cnsa-2026/draftkings/${{ steps.slug.outputs.service_slug }}:1.0.0 .

- name: Push Docker image to Artifact Registry
run: |
docker push us-east1-docker.pkg.dev/cnsa-2026/draftkings/${{ steps.slug.outputs.service_slug }}:0.0.1
docker push us-east1-docker.pkg.dev/cnsa-2026/draftkings/${{ steps.slug.outputs.service_slug }}:1.0.0

- name: Set Secret Placeholder
id: secret
Expand Down Expand Up @@ -103,7 +103,7 @@ jobs:
with:
service: "${{ steps.slug.outputs.service_slug }}"
region: "us-east1"
image: "us-east1-docker.pkg.dev/cnsa-2026/draftkings/${{ steps.slug.outputs.service_slug }}:0.0.1"
image: "us-east1-docker.pkg.dev/cnsa-2026/draftkings/${{ steps.slug.outputs.service_slug }}:1.0.0"
flags: |
--allow-unauthenticated
--ingress=all
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ La arquitectura de integración y despliegue continuo está diseñada para garan
**Deployment a Prod** (`node-cd.prod.yml`):
- Trigger: Se ejecuta si CI es exitoso en rama `main`
- Steps: Similares a Dev, con diferencias:
- Build Docker con tag versionado: `:0.0.1`
- Build Docker con tag versionado: `:1.0.0`
- Usa `Dockerfile.prod` optimizado
- Service: `dk-node` (sin sufijo -dev)
- Secret: `NODE_ENV_PROD`
Expand Down Expand Up @@ -385,7 +385,7 @@ on:

**Diferencias Dev/Prod:**
- **Dev:** Tag `:latest`, secret `_DEV`
- **Prod:** Tag `:0.0.1`, secret `_PROD`, Dockerfile.prod
- **Prod:** Tag `:1.0.0`, secret `_PROD`, Dockerfile.prod

---

Expand Down Expand Up @@ -473,7 +473,7 @@ El deployment de CORBA es **más complejo** que otros servicios debido a infraes
**Deployment a Prod** (`corba-cd.prod.yml`):
- VM: `dk-corba` (sin sufijo -dev)
- DNS: `dk-corba.cnsa-2026-dsa069.tech`
- Imagen: Tag `:0.0.1`
- Imagen: Tag `:1.0.0`
- Usa `Dockerfile.prod`

---
Expand Down
201 changes: 172 additions & 29 deletions api-node/draftKings_api/services/apiFootballService.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
import axios from "axios"; // Asegúrate de tener axios instalado (npm i axios)
import axios from "axios";
import Player from "../models/player";

interface TeamLeagueInfo {
teamName: string | null;
leagueName: string | null;
}

const API_BASE = "https://v3.football.api-sports.io";

export class ApiFootballService {
private apiKey = process.env.API_FOOTBALL_KEY;

// 1) Obtener y transformar datos de la API externa
private get headers() {
return {
"x-apisports-key": this.apiKey,
"x-rapidapi-host": "v3.football.api-sports.io",
};
}

async searchPlayers(search?: string): Promise<any[]> {

Check warning on line 21 in api-node/draftKings_api/services/apiFootballService.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

api-node/draftKings_api/services/apiFootballService.ts#L21

[@typescript-eslint/no-explicit-any] Unexpected any. Specify a different type.
const params: any = {};

Check warning on line 22 in api-node/draftKings_api/services/apiFootballService.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

api-node/draftKings_api/services/apiFootballService.ts#L22

[@typescript-eslint/no-explicit-any] Unexpected any. Specify a different type.
if (search) params.search = search;

try {
const response = await axios.get(
"https://v3.football.api-sports.io/players/profiles",
{
headers: {
"x-apisports-key": this.apiKey,
"x-rapidapi-host": "v3.football.api-sports.io",
},
params,
},
);
const response = await axios.get(`${API_BASE}/players/profiles`, {
headers: this.headers,
params,
});

const data = response.data;

if (!data || !data.response || !Array.isArray(data.response)) return [];

// Mapeo idéntico al que tenías en Angular
return data.response.map((item: any) => ({

Check warning on line 35 in api-node/draftKings_api/services/apiFootballService.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

api-node/draftKings_api/services/apiFootballService.ts#L35

[@typescript-eslint/no-explicit-any] Unexpected any. Specify a different type.
externalId: item.player.id,
name: item.player.name,
firstName: item.player.firstname || "",
lastName: item.player.lastname || "",
Expand All @@ -44,31 +51,167 @@
number: item.player.number || undefined,
}));
} catch (error) {
console.error("Error fetching from API-Football:", error);

Check warning on line 54 in api-node/draftKings_api/services/apiFootballService.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

api-node/draftKings_api/services/apiFootballService.ts#L54

[no-console] Unexpected console statement.
throw new Error("Failed to fetch players from external API", {
cause: error,
});
}
}

// 2) Importar los jugadores pasados desde el front a MongoDB
async resolveTeamAndLeague(
playerId: number,
nationality?: string,
): Promise<TeamLeagueInfo> {
const currentYear = new Date().getFullYear();

let teamsJson: any;

Check warning on line 67 in api-node/draftKings_api/services/apiFootballService.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

api-node/draftKings_api/services/apiFootballService.ts#L67

[@typescript-eslint/no-explicit-any] Unexpected any. Specify a different type.
try {
teamsJson = await this.getPlayerTeams(playerId);
} catch {
return { teamName: null, leagueName: null };
}

const teamsResponse = teamsJson?.response;
if (!teamsResponse || !Array.isArray(teamsResponse)) {
return { teamName: null, leagueName: null };
}

let teamName: string | null = null;
let teamId: number | null = null;
const yearsToTry = [currentYear, currentYear - 1];

for (const year of yearsToTry) {
let foundInYear = false;
for (const t of teamsResponse) {
const team = t.team;
const seasons = t.seasons;
if (!team || !seasons) continue;

const hasYear = seasons.includes(year);

if (
hasYear &&
nationality &&
!team.name?.includes(nationality)
) {
teamName = team.name;
teamId = team.id;
foundInYear = true;
break;
}
}
if (foundInYear) break;
}

if (teamId === null) {
return { teamName, leagueName: null };
}

let leaguesJson: any;

Check warning on line 110 in api-node/draftKings_api/services/apiFootballService.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

api-node/draftKings_api/services/apiFootballService.ts#L110

[@typescript-eslint/no-explicit-any] Unexpected any. Specify a different type.
try {
leaguesJson = await this.getLeaguesByTeam(teamId);
} catch {
return { teamName, leagueName: null };
}

const leaguesResponse = leaguesJson?.response;
if (!leaguesResponse || !Array.isArray(leaguesResponse)) {
return { teamName, leagueName: null };
}

let leagueName: string | null = null;
let maxDuration = -1;

for (const l of leaguesResponse) {
const league = l.league;
if (!league || league.type !== "League") continue;

const country = l.country;
if (country?.name === "World") continue;

for (const s of l.seasons) {
const seasonYear = s.year;
if (seasonYear === currentYear || seasonYear === currentYear - 1) {
const start = this.parseDate(s.start);
const end = this.parseDate(s.end);
if (start && end) {
const duration =
(end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24);
if (duration > maxDuration) {
maxDuration = duration;
leagueName = league.name;
}
}
}
}
}

return { teamName, leagueName };
}

private async getPlayerTeams(playerId: number): Promise<any> {

Check warning on line 152 in api-node/draftKings_api/services/apiFootballService.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

api-node/draftKings_api/services/apiFootballService.ts#L152

[@typescript-eslint/no-explicit-any] Unexpected any. Specify a different type.
const response = await axios.get(`${API_BASE}/players/teams`, {
headers: this.headers,
params: { player: playerId },
});
return response.data;
}

private async getLeaguesByTeam(teamId: number): Promise<any> {

Check warning on line 160 in api-node/draftKings_api/services/apiFootballService.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

api-node/draftKings_api/services/apiFootballService.ts#L160

[@typescript-eslint/no-explicit-any] Unexpected any. Specify a different type.
const response = await axios.get(`${API_BASE}/leagues`, {
headers: this.headers,
params: { team: teamId },
});
return response.data;
}

private parseDate(dateStr: string): Date | null {
if (!dateStr) return null;
try {
const d = new Date(dateStr);
return isNaN(d.getTime()) ? null : d;
} catch {
return null;
}
}

async importPlayers(players: any[]): Promise<void> {

Check warning on line 178 in api-node/draftKings_api/services/apiFootballService.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

api-node/draftKings_api/services/apiFootballService.ts#L178

[@typescript-eslint/no-explicit-any] Unexpected any. Specify a different type.
if (!players || players.length === 0) return;

// Transformamos los objetos planos al formato con GeoJSON para Mongoose
const docsToInsert = players.map((player) => ({
...player,
birthdate: player.birthdate ? new Date(player.birthdate) : null,
coords: {
type: "Point",
coordinates: [
Number(player.longitude || 0),
Number(player.latitude || 0),
],
},
}));

// Usamos insertMany para mayor eficiencia en base de datos
await Player.insertMany(docsToInsert);
const enrichedPlayers = await Promise.all(
players.map(async (player) => {
let teamName = player.team || null;
let leagueName = player.league || null;

if (
player.externalId &&
teamName === "API Football" &&
leagueName === "External"
) {
const info = await this.resolveTeamAndLeague(
player.externalId,
player.nationality,
);
teamName = info.teamName || teamName;
leagueName = info.leagueName || leagueName;
}

return {
...player,
team: teamName,
league: leagueName,
birthdate: player.birthdate ? new Date(player.birthdate) : null,
coords: {
type: "Point",
coordinates: [
Number(player.longitude || 0),
Number(player.latitude || 0),
],
},
};
}),
);

await Player.insertMany(enrichedPlayers);
}
}
Loading
Loading