diff --git a/docker-compose.network.yml b/docker-compose.network.yml index 9533000..4ffe74f 100644 --- a/docker-compose.network.yml +++ b/docker-compose.network.yml @@ -19,10 +19,10 @@ services: #################################### # Server & Network Config #################################### - server: + jukebox: restart: always image: ikehunter5/jukebox-server:latest - container_name: server-jbx-fe + container_name: jbx-server-fe environment: - NODE_ENV=network - PORT=9000 @@ -32,18 +32,23 @@ services: - JWT_SECRET_KEY=changeme - TOKEN_HEADER_KEY=Authorization - - - MONGO_URI=mongodb://root:changeme@mongo-network-jbx-fe:27017 + - KAFKA_BROKERS=kafka-jbx-fe:9092 - SPOTIFY_CLIENT_ID=${SPOTIFY_CLIENT_ID} - SPOTIFY_CLIENT_SECRET=${SPOTIFY_CLIENT_SECRET} - - SPOTIFY_REDIRECT_URI=http://localhost:8000/api/spotify/login-callback/ + + - PROXY_URL=http://proxy:8080 + - DB_HOST=jbx-network-db-fe + - DB_PORT=5432 + - DB_USER=devuser + - DB_PASS=devpass + - DB_NAME=devdatabase ports: - 9000:9000 depends_on: - - mongodb - - kafka + - postgres-jukebox + # - kafka networks: - cluster command: npm run start:network @@ -56,67 +61,67 @@ services: networks: - cluster depends_on: - - server + - jukebox - club-manager restart: always volumes: - client-dist-jbx:/vol/client - club-static:/vol/static environment: - - SERVER_URI=server-jbx-fe:9000 - - WEBSOCKET_URI=server-jbx-fe:9000 + - SERVER_URI=jbx-server-fe:9000 + - WEBSOCKET_URI=jbx-server-fe:9000 - CLUB_MANAGER_URI=club-app-network-fe:9000 # attach: false - mongodb: - image: mongo:6.0.9 - restart: always - container_name: mongo-network-jbx-fe - ports: - - 27017:27017 - volumes: - - mongo-data-fe:/data/db - environment: - - MONGO_INITDB_ROOT_USERNAME=root - - MONGO_INITDB_ROOT_PASSWORD=changeme - command: mongod --quiet --logpath /dev/null --bind_ip_all - networks: - - cluster + # mongodb: + # image: mongo:6.0.9 + # restart: always + # container_name: mongo-network-jbx-fe + # ports: + # - 27017:27017 + # volumes: + # - mongo-data-fe:/data/db + # environment: + # - MONGO_INITDB_ROOT_USERNAME=root + # - MONGO_INITDB_ROOT_PASSWORD=changeme + # command: mongod --quiet --logpath /dev/null --bind_ip_all + # networks: + # - cluster #################################### # Kafka Config #################################### - zookeeper: - image: confluentinc/cp-zookeeper:latest - container_name: zookeeper-jbx-fe - environment: - ZOOKEEPER_CLIENT_PORT: 2181 - ZOOKEEPER_TICK_TIME: 2000 - ports: - - 2181:2181 - networks: - - cluster - attach: false + # zookeeper: + # image: confluentinc/cp-zookeeper:latest + # container_name: zookeeper-jbx-fe + # environment: + # ZOOKEEPER_CLIENT_PORT: 2181 + # ZOOKEEPER_TICK_TIME: 2000 + # ports: + # - 2181:2181 + # networks: + # - cluster + # attach: false - kafka: - image: confluentinc/cp-kafka:latest - container_name: kafka-jbx-fe - depends_on: - - zookeeper - ports: - - 9092:9092 - - 29092:29092 - environment: - KAFKA_BROKER_ID: 1 - KAFKA_ZOOKEEPER_CONNECT: zookeeper-jbx-fe:2181 - KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092 - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT - KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 - KAFKA_AUTO_CREATE_TOPICS_ENABLE: true - networks: - - cluster - attach: false + # kafka: + # image: confluentinc/cp-kafka:latest + # container_name: kafka-jbx-fe + # depends_on: + # - zookeeper + # ports: + # - 9092:9092 + # - 29092:29092 + # environment: + # KAFKA_BROKER_ID: 1 + # KAFKA_ZOOKEEPER_CONNECT: zookeeper-jbx-fe:2181 + # KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092 + # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + # KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + # KAFKA_AUTO_CREATE_TOPICS_ENABLE: true + # networks: + # - cluster + # attach: false #################################### # Web Socket Config @@ -170,16 +175,27 @@ services: - DJANGO_SUPERUSER_EMAIL=${DJANGO_SUPERUSER_EMAIL:-admin@example.com} - DJANGO_SUPERUSER_PASS=${DJANGO_SUPERUSER_PASS:-changeme} depends_on: - - postgres + - postgres-clubs networks: - cluster + postgres-jukebox: + image: postgres:13-alpine + container_name: jbx-network-db-fe + volumes: + - postgres-jukebox:/var/lib/postgresql/data + environment: + - POSTGRES_DB=devdatabase + - POSTGRES_USER=devuser + - POSTGRES_PASSWORD=devpass + networks: + - cluster - postgres: + postgres-clubs: image: postgres:13-alpine container_name: club-app-network-db-fe volumes: - - postgres-osc-network:/var/lib/postgresql/data + - postgres-clubs:/var/lib/postgresql/data environment: - POSTGRES_DB=devdatabase - POSTGRES_USER=devuser @@ -193,8 +209,10 @@ volumes: mongo-data-fe: club-static: name: club-static-fe - postgres-osc-network: - name: postgres-osc-network-fe + postgres-clubs: + name: postgres-clubs-network-fe + postgres-jukebox: + name: postgres-jukebox-network-fe networks: cluster: diff --git a/src/mock/mock-clubs.ts b/src/mock/mock-clubs.ts index 98cb566..cfd5537 100644 --- a/src/mock/mock-clubs.ts +++ b/src/mock/mock-clubs.ts @@ -1,17 +1,17 @@ export const mockClubs: IClub[] = [ { - id: 'club-1', + id: 0, name: 'Test Club 1', - ownerId: '12345', + ownerId: 0, }, { - id: 'club-2', + id: 1, name: 'Test Club 2', - ownerId: '98765', + ownerId: 1, }, { - id: 'club-3', + id: 2, name: 'Test Club 3', - ownerId: '98765', + ownerId: 1, }, ] diff --git a/src/mock/mock-user.ts b/src/mock/mock-user.ts index 33744a6..67d024a 100644 --- a/src/mock/mock-user.ts +++ b/src/mock/mock-user.ts @@ -1,7 +1,7 @@ import { mockClubs } from './mock-clubs' export const mockUser: IUser & { token: string } = { - id: '12345', + id: 0, token: 'test-token', email: 'john@example.com', firstName: 'John', @@ -9,7 +9,7 @@ export const mockUser: IUser & { token: string } = { clubs: mockClubs.map((club) => ({ id: club.id, name: club.name, - ownerId: '12345', + ownerId: 0, })), image: 'https://static.vecteezy.com/system/resources/thumbnails/001/840/618/small_2x/picture-profile-icon-male-icon-human-or-people-sign-and-symbol-free-vector.jpg', diff --git a/src/network/network.ts b/src/network/network.ts index 4a72abe..5b670fe 100644 --- a/src/network/network.ts +++ b/src/network/network.ts @@ -13,6 +13,24 @@ import { import { NetworkRoutes } from './routes' import type { NetworkResponse } from './types' +interface SpotifyLink { + id: number + access_token: string + user_id: number + spotify_email: string + expires_in: number + expires_at: string + token_type: string +} + +interface Jukebox { + id: number + name: string + club_id: number + spotify_links: SpotifyLink[] + active_spotify_link?: SpotifyLink +} + export class Network { static instance: Network protected routes: typeof NetworkRoutes @@ -108,7 +126,7 @@ export class Network { return ok(token) } - const res = await this.sendRequest(NetworkRoutes.user.token, 'POST', { + const res = await this.sendRequest(this.routes.user.token, 'POST', { email, password, }) @@ -135,7 +153,7 @@ export class Network { if (this.env === 'dev') { await sleep(1000) return { - id: Date.now().toString(), + id: Date.now(), email: mockUser.email, firstName: mockUser.firstName, lastName: mockUser.lastName, @@ -144,46 +162,46 @@ export class Network { clubs: mockUser.clubs, } } - const res = await this.sendRequest(NetworkRoutes.user.details) + const res = await this.sendRequest(this.routes.user.details) console.log('user res:', res) return { id: res?.data.id, email: res?.data.email, - firstName: res?.data.firstName, - lastName: res?.data.lastName, + firstName: res?.data.first_name, + lastName: res?.data.last_name, image: res?.data.image ?? 'https://alliancebjjmn.com/wp-content/uploads/2019/07/placeholder-profile-sq-491x407.jpg', clubs: Array.from(res?.data.clubs).map((club: any) => ({ - id: String(club.id), + id: club.id, name: String(club.name), - ownerId: String(club.ownerId), + ownerId: club.owner_id, })), } } - public async sendGetClubInfo(clubId: string): Promise { + public async sendGetClubInfo(clubId: number): Promise { if (this.env === 'dev') { throw new NotImplementedError('network.sendGetClubInfo') } - const res = await this.sendRequest(NetworkRoutes.club.info(clubId)) + const res = await this.sendRequest(this.routes.club.info(clubId)) return { id: res.data.id, name: res.data.name, - ownerId: res.data.ownerId, + ownerId: res.data.owner_id, } } - public async sendGetSpotifyToken(clubId: string): Promise { + public async sendGetSpotifyToken(clubId: number): Promise { if (this.env === 'dev') { await sleep(1000) return { - id: '66e72f18a7c93a68835d630d', + id: 0, accessToken: String(import.meta.env.VITE_SPOTIFY_ACCESS_TOKEN), - userId: '66da2b580235f4ff7270460d', + userId: 0, spotifyEmail: 'user@example.com', expiresIn: 3600, tokenType: 'Bearer', @@ -191,15 +209,27 @@ export class Network { } } - const res = await this.sendRequest(NetworkRoutes.club.spotifyAuth(clubId)) + const res = await this.sendRequest(this.routes.jukebox.list) + const jukeboxes = res.data as Jukebox[] + const selectedJbx = jukeboxes.find((jbx) => +jbx.club_id === +clubId) + + // TODO: Handle jukeboxes separately from clubs + if (!selectedJbx?.active_spotify_link) { + throw new Error('No spotify connections found.') + } + + const refreshedRes = await this.sendRequest( + this.routes.jukebox.activeLink(selectedJbx.id), + ) + return { - id: res?.data.id, - accessToken: res?.data.accessToken, - userId: res?.data.userId, - spotifyEmail: res?.data.spotifyEmail, - expiresIn: res?.data.expiresIn, - tokenType: res?.data.tokenType, - expiresAt: res?.data.expiresAt, + id: refreshedRes.data?.id, + accessToken: refreshedRes.data?.access_token, + userId: refreshedRes.data?.user_id, + spotifyEmail: refreshedRes.data?.spotify_email, + expiresIn: refreshedRes.data?.expires_in, + tokenType: refreshedRes.data?.token_type, + expiresAt: new Date(refreshedRes.data?.expires_at).getTime(), } } } diff --git a/src/network/routes.ts b/src/network/routes.ts index ab48dc5..38576bb 100644 --- a/src/network/routes.ts +++ b/src/network/routes.ts @@ -8,7 +8,10 @@ export const NetworkRoutes = { details: `${base}/user/me/`, }, club: { - info: (id: string) => `${base}/club/clubs/${id}`, - spotifyAuth: (id: string) => `${base}/club/${id}/spotify/auth`, + info: (id: number) => `${base}/club/clubs/${id}`, + }, + jukebox: { + list: `${base}/jukebox/jukeboxes`, + activeLink: (id: number) => `${base}/jukebox/${id}/active-link/`, }, } diff --git a/src/store/club/clubThunks.ts b/src/store/club/clubThunks.ts index 07c8069..b300a42 100644 --- a/src/store/club/clubThunks.ts +++ b/src/store/club/clubThunks.ts @@ -10,14 +10,14 @@ export const thunkFetchAdminClubs = createAsyncThunk( export const thunkFetchClubInfo = createAsyncThunk( 'club/fetchClubInfo', - async (clubId: string) => { + async (clubId: number) => { return await network.sendGetClubInfo(clubId) }, ) export const thunkFetchClubSpotifyAuth = createAsyncThunk( 'club/fetchClubSpotifyAuth', - async (clubId: string) => { + async (clubId: number) => { return await network.sendGetSpotifyToken(clubId) }, ) diff --git a/src/types/group.d.ts b/src/types/group.d.ts index b7475ed..6d1516a 100644 --- a/src/types/group.d.ts +++ b/src/types/group.d.ts @@ -1,18 +1,18 @@ declare interface IClubFields { name: string - ownerId: string + ownerId: number spotifyAuthId?: string defaultDeviceId?: string } declare interface IClub extends IClubFields { - id: string + id: number } declare interface ISpotifyAuth { - id: string + id: number accessToken: string - userId: string + userId: number spotifyEmail: string expiresIn: number tokenType: string diff --git a/src/types/users.d.ts b/src/types/users.d.ts index b443a97..c633177 100644 --- a/src/types/users.d.ts +++ b/src/types/users.d.ts @@ -1,5 +1,5 @@ declare interface IUser { - id: string + id: number email: string firstName?: string lastName?: string