mirror of
https://github.com/apricote/Listory.git
synced 2026-02-07 10:17:02 +00:00
chore(api): update dependencies
This commit is contained in:
parent
05f230a7ce
commit
d881a78757
37 changed files with 4804 additions and 2700 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
##################
|
##################
|
||||||
## common
|
## common
|
||||||
##################
|
##################
|
||||||
FROM node:12-alpine as common
|
FROM node:14-alpine as common
|
||||||
LABEL org.label-schema.schema-version="1.0" \
|
LABEL org.label-schema.schema-version="1.0" \
|
||||||
org.label-schema.name="listory" \
|
org.label-schema.name="listory" \
|
||||||
stage="common"
|
stage="common"
|
||||||
|
|
|
||||||
7218
package-lock.json
generated
7218
package-lock.json
generated
File diff suppressed because it is too large
Load diff
62
package.json
62
package.json
|
|
@ -7,7 +7,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "rimraf dist",
|
"prebuild": "rimraf dist",
|
||||||
"build": "nest build",
|
"build": "nest build",
|
||||||
"format": "prettier --write \"apps/**/*.ts\" \"libs/**/*.ts\"",
|
"format": "prettier --write \"src/**/*.ts\"",
|
||||||
"start": "nest start",
|
"start": "nest start",
|
||||||
"start:dev": "nest start --watch",
|
"start:dev": "nest start --watch",
|
||||||
"start:debug": "nest start --debug --watch",
|
"start:debug": "nest start --debug --watch",
|
||||||
|
|
@ -20,47 +20,47 @@
|
||||||
"test:e2e": "jest --config ./apps/listory/test/jest-e2e.json"
|
"test:e2e": "jest --config ./apps/listory/test/jest-e2e.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^6.7.2",
|
"@nestjs/common": "^7.0.9",
|
||||||
"@nestjs/config": "^0.1.0",
|
"@nestjs/config": "^0.4.0",
|
||||||
"@nestjs/core": "^6.7.2",
|
"@nestjs/core": "^7.0.9",
|
||||||
"@nestjs/jwt": "^6.1.1",
|
"@nestjs/jwt": "^7.0.0",
|
||||||
"@nestjs/passport": "^6.1.1",
|
"@nestjs/passport": "^7.0.0",
|
||||||
"@nestjs/platform-express": "^6.7.2",
|
"@nestjs/platform-express": "^7.0.9",
|
||||||
"@nestjs/schedule": "^0.2.0",
|
"@nestjs/schedule": "^0.3.1",
|
||||||
"@nestjs/swagger": "^4.2.3",
|
"@nestjs/swagger": "^4.5.3",
|
||||||
"@nestjs/typeorm": "^6.2.0",
|
"@nestjs/typeorm": "^7.0.0",
|
||||||
"class-transformer": "^0.2.3",
|
"class-transformer": "^0.2.3",
|
||||||
"class-validator": "^0.11.0",
|
"class-validator": "^0.12.2",
|
||||||
"passport": "^0.4.1",
|
"passport": "^0.4.1",
|
||||||
"passport-jwt": "^4.0.0",
|
"passport-jwt": "^4.0.0",
|
||||||
"passport-spotify": "^1.1.0",
|
"passport-spotify": "^1.1.0",
|
||||||
"pg": "^7.17.1",
|
"pg": "^8.0.3",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rimraf": "^3.0.0",
|
"rimraf": "^3.0.2",
|
||||||
"rxjs": "^6.5.3",
|
"rxjs": "^6.5.5",
|
||||||
"swagger-ui-express": "^4.1.3",
|
"swagger-ui-express": "^4.1.4",
|
||||||
"typeorm": "^0.2.22"
|
"typeorm": "^0.2.24"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^6.9.0",
|
"@nestjs/cli": "^7.1.4",
|
||||||
"@nestjs/schematics": "^6.7.0",
|
"@nestjs/schematics": "^7.0.0",
|
||||||
"@nestjs/testing": "^6.7.1",
|
"@nestjs/testing": "^7.0.9",
|
||||||
"@types/express": "^4.17.1",
|
"@types/express": "^4.17.6",
|
||||||
"@types/jest": "^24.0.18",
|
"@types/jest": "^25.2.1",
|
||||||
"@types/node": "^12.7.5",
|
"@types/node": "^13.13.4",
|
||||||
"@types/passport-jwt": "^3.0.3",
|
"@types/passport-jwt": "^3.0.3",
|
||||||
"@types/supertest": "^2.0.8",
|
"@types/supertest": "^2.0.9",
|
||||||
"jest": "^24.9.0",
|
"jest": "^25.5.4",
|
||||||
"prettier": "^1.18.2",
|
"prettier": "^2.0.5",
|
||||||
"supertest": "^4.0.2",
|
"supertest": "^4.0.2",
|
||||||
"ts-jest": "^24.1.0",
|
"ts-jest": "^25.4.0",
|
||||||
"ts-loader": "^6.1.1",
|
"ts-loader": "^7.0.2",
|
||||||
"ts-node": "^8.4.1",
|
"ts-node": "^8.9.1",
|
||||||
"tsconfig-paths": "^3.9.0",
|
"tsconfig-paths": "^3.9.0",
|
||||||
"tslint": "^5.20.0",
|
"tslint": "^6.1.2",
|
||||||
"tslint-config-prettier": "^1.18.0",
|
"tslint-config-prettier": "^1.18.0",
|
||||||
"tslint-plugin-prettier": "^2.1.0",
|
"tslint-plugin-prettier": "^2.3.0",
|
||||||
"typescript": "^3.6.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": [
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import { UsersModule } from "./users/users.module";
|
||||||
UsersModule,
|
UsersModule,
|
||||||
SourcesModule,
|
SourcesModule,
|
||||||
MusicLibraryModule,
|
MusicLibraryModule,
|
||||||
ListensModule
|
ListensModule,
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ export class AuthController {
|
||||||
maxAge: 15 * 60 * 1000,
|
maxAge: 15 * 60 * 1000,
|
||||||
|
|
||||||
// Must be readable by SPA
|
// Must be readable by SPA
|
||||||
httpOnly: false
|
httpOnly: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Redirect User to SPA
|
// Redirect User to SPA
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,14 @@ import { SpotifyStrategy } from "./spotify.strategy";
|
||||||
JwtModule.registerAsync({
|
JwtModule.registerAsync({
|
||||||
useFactory: (config: ConfigService) => ({
|
useFactory: (config: ConfigService) => ({
|
||||||
secret: config.get<string>("JWT_SECRET"),
|
secret: config.get<string>("JWT_SECRET"),
|
||||||
signOptions: { expiresIn: config.get<string>("JWT_EXPIRATION_TIME") }
|
signOptions: { expiresIn: config.get<string>("JWT_EXPIRATION_TIME") },
|
||||||
}),
|
}),
|
||||||
inject: [ConfigService]
|
inject: [ConfigService],
|
||||||
}),
|
}),
|
||||||
UsersModule
|
UsersModule,
|
||||||
],
|
],
|
||||||
providers: [AuthService, SpotifyStrategy, JwtStrategy],
|
providers: [AuthService, SpotifyStrategy, JwtStrategy],
|
||||||
exports: [PassportModule],
|
exports: [PassportModule],
|
||||||
controllers: [AuthController]
|
controllers: [AuthController],
|
||||||
})
|
})
|
||||||
export class AuthModule {}
|
export class AuthModule {}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ describe("AuthService", () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [AuthService]
|
providers: [AuthService],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<AuthService>(AuthService);
|
service = module.get<AuthService>(AuthService);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export class AuthService {
|
||||||
async spotifyLogin({
|
async spotifyLogin({
|
||||||
accessToken,
|
accessToken,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
profile
|
profile,
|
||||||
}: LoginDto): Promise<User> {
|
}: LoginDto): Promise<User> {
|
||||||
const user = await this.usersService.createOrUpdate({
|
const user = await this.usersService.createOrUpdate({
|
||||||
displayName: profile.displayName,
|
displayName: profile.displayName,
|
||||||
|
|
@ -22,8 +22,8 @@ export class AuthService {
|
||||||
spotify: {
|
spotify: {
|
||||||
id: profile.id,
|
id: profile.id,
|
||||||
accessToken,
|
accessToken,
|
||||||
refreshToken
|
refreshToken,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
|
|
@ -33,7 +33,7 @@ export class AuthService {
|
||||||
const payload = {
|
const payload = {
|
||||||
sub: user.id,
|
sub: user.id,
|
||||||
name: user.displayName,
|
name: user.displayName,
|
||||||
picture: user.photo
|
picture: user.photo,
|
||||||
};
|
};
|
||||||
|
|
||||||
const token = await this.jwtService.signAsync(payload);
|
const token = await this.jwtService.signAsync(payload);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
import { createParamDecorator } from "@nestjs/common";
|
import { createParamDecorator, ExecutionContext } from "@nestjs/common";
|
||||||
|
|
||||||
export const ReqUser = createParamDecorator((data, req) => req.user);
|
export const ReqUser = createParamDecorator<void>(
|
||||||
|
(_: void, ctx: ExecutionContext) => {
|
||||||
|
const request = ctx.switchToHttp().getRequest();
|
||||||
|
return request.user;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,13 @@ import { AuthService } from "./auth.service";
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly config: ConfigService,
|
private readonly authService: AuthService,
|
||||||
private readonly authService: AuthService
|
config: ConfigService
|
||||||
) {
|
) {
|
||||||
super({
|
super({
|
||||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
ignoreExpiration: false,
|
ignoreExpiration: false,
|
||||||
secretOrKey: config.get<string>("JWT_SECRET")
|
secretOrKey: config.get<string>("JWT_SECRET"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,14 @@ export class SpotifyStrategy extends PassportStrategy(Strategy) {
|
||||||
super({
|
super({
|
||||||
clientID: config.get<string>("SPOTIFY_CLIENT_ID"),
|
clientID: config.get<string>("SPOTIFY_CLIENT_ID"),
|
||||||
clientSecret: config.get<string>("SPOTIFY_CLIENT_SECRET"),
|
clientSecret: config.get<string>("SPOTIFY_CLIENT_SECRET"),
|
||||||
callbackURL: `${config.get<string>("BASE_DOMAIN") ||
|
callbackURL: `${
|
||||||
"http://localhost:3000"}/api/v1/auth/spotify/callback`,
|
config.get<string>("BASE_DOMAIN") || "http://localhost:3000"
|
||||||
|
}/api/v1/auth/spotify/callback`,
|
||||||
scope: [
|
scope: [
|
||||||
"user-read-private",
|
"user-read-private",
|
||||||
"user-read-email",
|
"user-read-email",
|
||||||
"user-read-recently-played"
|
"user-read-recently-played",
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,7 +28,7 @@ export class SpotifyStrategy extends PassportStrategy(Strategy) {
|
||||||
return await this.authService.spotifyLogin({
|
return await this.authService.spotifyLogin({
|
||||||
accessToken,
|
accessToken,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
profile
|
profile,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export const DatabaseModule = TypeOrmModule.forRootAsync({
|
||||||
// Migrations
|
// Migrations
|
||||||
// migrationsRun: true,
|
// migrationsRun: true,
|
||||||
// migrations: [join(__dirname, "migrations", "*.{ts,js}")],
|
// migrations: [join(__dirname, "migrations", "*.{ts,js}")],
|
||||||
synchronize: true
|
synchronize: true,
|
||||||
}),
|
}),
|
||||||
inject: [ConfigService]
|
inject: [ConfigService],
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import {
|
||||||
Entity,
|
Entity,
|
||||||
Index,
|
Index,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
PrimaryGeneratedColumn
|
PrimaryGeneratedColumn,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { Track } from "../music-library/track.entity";
|
import { Track } from "../music-library/track.entity";
|
||||||
import { User } from "../users/user.entity";
|
import { User } from "../users/user.entity";
|
||||||
|
|
@ -14,10 +14,10 @@ export class Listen {
|
||||||
@PrimaryGeneratedColumn("uuid")
|
@PrimaryGeneratedColumn("uuid")
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
@ManyToOne(type => Track)
|
@ManyToOne((type) => Track)
|
||||||
track: Track;
|
track: Track;
|
||||||
|
|
||||||
@ManyToOne(type => User)
|
@ManyToOne((type) => User)
|
||||||
user: User;
|
user: User;
|
||||||
|
|
||||||
@Column({ type: "timestamp" })
|
@Column({ type: "timestamp" })
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,6 @@ import { ListenRepository } from "./listen.repository";
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([ListenRepository])],
|
imports: [TypeOrmModule.forFeature([ListenRepository])],
|
||||||
providers: [ListensService],
|
providers: [ListensService],
|
||||||
exports: [ListensService]
|
exports: [ListensService],
|
||||||
})
|
})
|
||||||
export class ListensModule {}
|
export class ListensModule {}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ describe("ListensService", () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [ListensService]
|
providers: [ListensService],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<ListensService>(ListensService);
|
service = module.get<ListensService>(ListensService);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export class ListensService {
|
||||||
async createListen({
|
async createListen({
|
||||||
user,
|
user,
|
||||||
track,
|
track,
|
||||||
playedAt
|
playedAt,
|
||||||
}: CreateListenDto): Promise<Listen> {
|
}: CreateListenDto): Promise<Listen> {
|
||||||
const listen = this.listenRepository.create();
|
const listen = this.listenRepository.create();
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@ export class ListensService {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code === "23505") {
|
if (err.code === "23505") {
|
||||||
return this.listenRepository.findOne({
|
return this.listenRepository.findOne({
|
||||||
where: { user, track, playedAt }
|
where: { user, track, playedAt },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import {
|
||||||
Column,
|
Column,
|
||||||
ManyToMany,
|
ManyToMany,
|
||||||
JoinTable,
|
JoinTable,
|
||||||
OneToMany
|
OneToMany,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { SpotifyLibraryDetails } from "src/sources/spotify/spotify-library-details.entity";
|
import { SpotifyLibraryDetails } from "src/sources/spotify/spotify-library-details.entity";
|
||||||
import { Artist } from "./artist.entity";
|
import { Artist } from "./artist.entity";
|
||||||
|
|
@ -18,19 +18,13 @@ export class Album {
|
||||||
@Column()
|
@Column()
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@ManyToMany(
|
@ManyToMany((type) => Artist, (artist) => artist.albums)
|
||||||
type => Artist,
|
|
||||||
artist => artist.albums
|
|
||||||
)
|
|
||||||
@JoinTable()
|
@JoinTable()
|
||||||
artists: Artist[];
|
artists: Artist[];
|
||||||
|
|
||||||
@OneToMany(
|
@OneToMany((type) => Track, (track) => track.album)
|
||||||
type => Track,
|
|
||||||
track => track.album
|
|
||||||
)
|
|
||||||
tracks: Track[];
|
tracks: Track[];
|
||||||
|
|
||||||
@Column(type => SpotifyLibraryDetails)
|
@Column((type) => SpotifyLibraryDetails)
|
||||||
spotify: SpotifyLibraryDetails;
|
spotify: SpotifyLibraryDetails;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,9 @@ export class Artist {
|
||||||
@Column()
|
@Column()
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@ManyToMany(
|
@ManyToMany((type) => Album, (album) => album.artists)
|
||||||
type => Album,
|
|
||||||
album => album.artists
|
|
||||||
)
|
|
||||||
albums: Album[];
|
albums: Album[];
|
||||||
|
|
||||||
@Column(type => SpotifyLibraryDetails)
|
@Column((type) => SpotifyLibraryDetails)
|
||||||
spotify: SpotifyLibraryDetails;
|
spotify: SpotifyLibraryDetails;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ import { TrackRepository } from "./track.repository";
|
||||||
TypeOrmModule.forFeature([
|
TypeOrmModule.forFeature([
|
||||||
AlbumRepository,
|
AlbumRepository,
|
||||||
ArtistRepository,
|
ArtistRepository,
|
||||||
TrackRepository
|
TrackRepository,
|
||||||
])
|
]),
|
||||||
],
|
],
|
||||||
providers: [MusicLibraryService],
|
providers: [MusicLibraryService],
|
||||||
exports: [MusicLibraryService]
|
exports: [MusicLibraryService],
|
||||||
})
|
})
|
||||||
export class MusicLibraryModule {}
|
export class MusicLibraryModule {}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ describe("MusicLibraryService", () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [MusicLibraryService]
|
providers: [MusicLibraryService],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<MusicLibraryService>(MusicLibraryService);
|
service = module.get<MusicLibraryService>(MusicLibraryService);
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export class MusicLibraryService {
|
||||||
|
|
||||||
async findArtist(query: FindArtistDto): Promise<Artist | undefined> {
|
async findArtist(query: FindArtistDto): Promise<Artist | undefined> {
|
||||||
return this.artistRepository.findOne({
|
return this.artistRepository.findOne({
|
||||||
where: { spotify: { id: query.spotify.id } }
|
where: { spotify: { id: query.spotify.id } },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,7 +41,7 @@ export class MusicLibraryService {
|
||||||
|
|
||||||
async findAlbum(query: FindAlbumDto): Promise<Album | undefined> {
|
async findAlbum(query: FindAlbumDto): Promise<Album | undefined> {
|
||||||
return this.albumRepository.findOne({
|
return this.albumRepository.findOne({
|
||||||
where: { spotify: { id: query.spotify.id } }
|
where: { spotify: { id: query.spotify.id } },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ export class MusicLibraryService {
|
||||||
|
|
||||||
async findTrack(query: FindTrackDto): Promise<Track | undefined> {
|
async findTrack(query: FindTrackDto): Promise<Track | undefined> {
|
||||||
return this.trackRepository.findOne({
|
return this.trackRepository.findOne({
|
||||||
where: { spotify: { id: query.spotify.id } }
|
where: { spotify: { id: query.spotify.id } },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import {
|
||||||
JoinTable,
|
JoinTable,
|
||||||
ManyToMany,
|
ManyToMany,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
PrimaryGeneratedColumn
|
PrimaryGeneratedColumn,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { Album } from "./album.entity";
|
import { Album } from "./album.entity";
|
||||||
import { Artist } from "./artist.entity";
|
import { Artist } from "./artist.entity";
|
||||||
|
|
@ -18,16 +18,13 @@ export class Track {
|
||||||
@Column()
|
@Column()
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@ManyToOne(
|
@ManyToOne((type) => Album, (album) => album.tracks)
|
||||||
type => Album,
|
|
||||||
album => album.tracks
|
|
||||||
)
|
|
||||||
album: Album;
|
album: Album;
|
||||||
|
|
||||||
@ManyToMany(type => Artist)
|
@ManyToMany((type) => Artist)
|
||||||
@JoinTable()
|
@JoinTable()
|
||||||
artists: Artist[];
|
artists: Artist[];
|
||||||
|
|
||||||
@Column(type => SpotifyLibraryDetails)
|
@Column((type) => SpotifyLibraryDetails)
|
||||||
spotify?: SpotifyLibraryDetails;
|
spotify?: SpotifyLibraryDetails;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,6 @@ import { Module } from "@nestjs/common";
|
||||||
import { SpotifyModule } from "./spotify/spotify.module";
|
import { SpotifyModule } from "./spotify/spotify.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [SpotifyModule]
|
imports: [SpotifyModule],
|
||||||
})
|
})
|
||||||
export class SourcesModule {}
|
export class SourcesModule {}
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ import { SpotifyApiService } from "./spotify-api.service";
|
||||||
HttpModule.registerAsync({
|
HttpModule.registerAsync({
|
||||||
useFactory: () => ({
|
useFactory: () => ({
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
baseURL: "https://api.spotify.com/"
|
baseURL: "https://api.spotify.com/",
|
||||||
})
|
}),
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
providers: [SpotifyApiService],
|
providers: [SpotifyApiService],
|
||||||
exports: [SpotifyApiService]
|
exports: [SpotifyApiService],
|
||||||
})
|
})
|
||||||
export class SpotifyApiModule {}
|
export class SpotifyApiModule {}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
import { SpotifyApiService } from './spotify-api.service';
|
import { SpotifyApiService } from "./spotify-api.service";
|
||||||
|
|
||||||
describe('SpotifyApiService', () => {
|
describe("SpotifyApiService", () => {
|
||||||
let service: SpotifyApiService;
|
let service: SpotifyApiService;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|
@ -12,7 +12,7 @@ describe('SpotifyApiService', () => {
|
||||||
service = module.get<SpotifyApiService>(SpotifyApiService);
|
service = module.get<SpotifyApiService>(SpotifyApiService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be defined', () => {
|
it("should be defined", () => {
|
||||||
expect(service).toBeDefined();
|
expect(service).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,12 @@ export class SpotifyApiService {
|
||||||
|
|
||||||
async getRecentlyPlayedTracks({
|
async getRecentlyPlayedTracks({
|
||||||
accessToken,
|
accessToken,
|
||||||
lastRefreshTime
|
lastRefreshTime,
|
||||||
}: SpotifyConnection): Promise<PlayHistoryObject[]> {
|
}: SpotifyConnection): Promise<PlayHistoryObject[]> {
|
||||||
console.log("SpotifyApiService#getRecentlyPlayedTracks");
|
console.log("SpotifyApiService#getRecentlyPlayedTracks");
|
||||||
|
|
||||||
const parameters: { limit: number; after?: number } = {
|
const parameters: { limit: number; after?: number } = {
|
||||||
limit: 50
|
limit: 50,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (lastRefreshTime) {
|
if (lastRefreshTime) {
|
||||||
|
|
@ -33,7 +33,7 @@ export class SpotifyApiService {
|
||||||
const history = await this.httpService
|
const history = await this.httpService
|
||||||
.get<PagingObject<PlayHistoryObject>>(`v1/me/player/recently-played`, {
|
.get<PagingObject<PlayHistoryObject>>(`v1/me/player/recently-played`, {
|
||||||
headers: { Authorization: `Bearer ${accessToken}` },
|
headers: { Authorization: `Bearer ${accessToken}` },
|
||||||
params: parameters
|
params: parameters,
|
||||||
})
|
})
|
||||||
.toPromise();
|
.toPromise();
|
||||||
|
|
||||||
|
|
@ -47,7 +47,7 @@ export class SpotifyApiService {
|
||||||
console.log("SpotifyApiService#getArtist");
|
console.log("SpotifyApiService#getArtist");
|
||||||
const artist = await this.httpService
|
const artist = await this.httpService
|
||||||
.get<ArtistObject>(`v1/artists/${spotifyID}`, {
|
.get<ArtistObject>(`v1/artists/${spotifyID}`, {
|
||||||
headers: { Authorization: `Bearer ${accessToken}` }
|
headers: { Authorization: `Bearer ${accessToken}` },
|
||||||
})
|
})
|
||||||
.toPromise();
|
.toPromise();
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ export class SpotifyApiService {
|
||||||
|
|
||||||
const album = await this.httpService
|
const album = await this.httpService
|
||||||
.get<AlbumObject>(`v1/albums/${spotifyID}`, {
|
.get<AlbumObject>(`v1/albums/${spotifyID}`, {
|
||||||
headers: { Authorization: `Bearer ${accessToken}` }
|
headers: { Authorization: `Bearer ${accessToken}` },
|
||||||
})
|
})
|
||||||
.toPromise();
|
.toPromise();
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ export class SpotifyApiService {
|
||||||
|
|
||||||
const track = await this.httpService
|
const track = await this.httpService
|
||||||
.get<TrackObject>(`v1/tracks/${spotifyID}`, {
|
.get<TrackObject>(`v1/tracks/${spotifyID}`, {
|
||||||
headers: { Authorization: `Bearer ${accessToken}` }
|
headers: { Authorization: `Bearer ${accessToken}` },
|
||||||
})
|
})
|
||||||
.toPromise();
|
.toPromise();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ import { SpotifyAuthService } from "./spotify-auth.service";
|
||||||
HttpModule.registerAsync({
|
HttpModule.registerAsync({
|
||||||
useFactory: () => ({
|
useFactory: () => ({
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
baseURL: "https://accounts.spotify.com/"
|
baseURL: "https://accounts.spotify.com/",
|
||||||
})
|
}),
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
providers: [SpotifyAuthService],
|
providers: [SpotifyAuthService],
|
||||||
exports: [SpotifyAuthService]
|
exports: [SpotifyAuthService],
|
||||||
})
|
})
|
||||||
export class SpotifyAuthModule {}
|
export class SpotifyAuthModule {}
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ export class SpotifyAuthService {
|
||||||
{
|
{
|
||||||
auth: {
|
auth: {
|
||||||
username: this.clientID,
|
username: this.clientID,
|
||||||
password: this.clientSecret
|
password: this.clientSecret,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.toPromise();
|
.toPromise();
|
||||||
|
|
@ -40,8 +40,8 @@ export class SpotifyAuthService {
|
||||||
{
|
{
|
||||||
auth: {
|
auth: {
|
||||||
username: this.clientID,
|
username: this.clientID,
|
||||||
password: this.clientSecret
|
password: this.clientSecret,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.toPromise();
|
.toPromise();
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ import { SpotifyAuthModule } from "./spotify-auth/spotify-auth.module";
|
||||||
ListensModule,
|
ListensModule,
|
||||||
MusicLibraryModule,
|
MusicLibraryModule,
|
||||||
SpotifyApiModule,
|
SpotifyApiModule,
|
||||||
SpotifyAuthModule
|
SpotifyAuthModule,
|
||||||
],
|
],
|
||||||
providers: [SpotifyService]
|
providers: [SpotifyService],
|
||||||
})
|
})
|
||||||
export class SpotifyModule {}
|
export class SpotifyModule {}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ describe("SpotifyService", () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [SpotifyService]
|
providers: [SpotifyService],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<SpotifyService>(SpotifyService);
|
service = module.get<SpotifyService>(SpotifyService);
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ export class SpotifyService {
|
||||||
);
|
);
|
||||||
await this.usersService.updateSpotifyConnection(user, {
|
await this.usersService.updateSpotifyConnection(user, {
|
||||||
...user.spotify,
|
...user.spotify,
|
||||||
accessToken
|
accessToken,
|
||||||
});
|
});
|
||||||
await this.processUser(user, false);
|
await this.processUser(user, false);
|
||||||
}
|
}
|
||||||
|
|
@ -62,13 +62,13 @@ export class SpotifyService {
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
playHistory.map(async history => {
|
playHistory.map(async (history) => {
|
||||||
const track = await this.importTrack(history.track.id);
|
const track = await this.importTrack(history.track.id);
|
||||||
|
|
||||||
this.listensService.createListen({
|
this.listensService.createListen({
|
||||||
user,
|
user,
|
||||||
track,
|
track,
|
||||||
playedAt: new Date(history.played_at)
|
playedAt: new Date(history.played_at),
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
|
|
@ -79,19 +79,19 @@ export class SpotifyService {
|
||||||
|
|
||||||
const newestPlayTime = new Date(
|
const newestPlayTime = new Date(
|
||||||
playHistory
|
playHistory
|
||||||
.map(history => history.played_at)
|
.map((history) => history.played_at)
|
||||||
.sort()
|
.sort()
|
||||||
.pop()
|
.pop()
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("newestPlayTime", {
|
console.log("newestPlayTime", {
|
||||||
newestPlayTime,
|
newestPlayTime,
|
||||||
times: playHistory.map(history => history.played_at).sort()
|
times: playHistory.map((history) => history.played_at).sort(),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.usersService.updateSpotifyConnection(user, {
|
this.usersService.updateSpotifyConnection(user, {
|
||||||
...user.spotify,
|
...user.spotify,
|
||||||
lastRefreshTime: newestPlayTime
|
lastRefreshTime: newestPlayTime,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,7 +100,7 @@ export class SpotifyService {
|
||||||
retryOnExpiredToken: boolean = true
|
retryOnExpiredToken: boolean = true
|
||||||
): Promise<Track> {
|
): Promise<Track> {
|
||||||
const track = await this.musicLibraryService.findTrack({
|
const track = await this.musicLibraryService.findTrack({
|
||||||
spotify: { id: spotifyID }
|
spotify: { id: spotifyID },
|
||||||
});
|
});
|
||||||
if (track) {
|
if (track) {
|
||||||
return track;
|
return track;
|
||||||
|
|
@ -129,7 +129,7 @@ export class SpotifyService {
|
||||||
spotifyTrack.artists.map(({ id: artistID }) =>
|
spotifyTrack.artists.map(({ id: artistID }) =>
|
||||||
this.importArtist(artistID)
|
this.importArtist(artistID)
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return this.musicLibraryService.createTrack({
|
return this.musicLibraryService.createTrack({
|
||||||
|
|
@ -140,8 +140,8 @@ export class SpotifyService {
|
||||||
id: spotifyTrack.id,
|
id: spotifyTrack.id,
|
||||||
uri: spotifyTrack.uri,
|
uri: spotifyTrack.uri,
|
||||||
type: spotifyTrack.type,
|
type: spotifyTrack.type,
|
||||||
href: spotifyTrack.href
|
href: spotifyTrack.href,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,7 +150,7 @@ export class SpotifyService {
|
||||||
retryOnExpiredToken: boolean = true
|
retryOnExpiredToken: boolean = true
|
||||||
): Promise<Album> {
|
): Promise<Album> {
|
||||||
const album = await this.musicLibraryService.findAlbum({
|
const album = await this.musicLibraryService.findAlbum({
|
||||||
spotify: { id: spotifyID }
|
spotify: { id: spotifyID },
|
||||||
});
|
});
|
||||||
if (album) {
|
if (album) {
|
||||||
return album;
|
return album;
|
||||||
|
|
@ -186,8 +186,8 @@ export class SpotifyService {
|
||||||
id: spotifyAlbum.id,
|
id: spotifyAlbum.id,
|
||||||
uri: spotifyAlbum.uri,
|
uri: spotifyAlbum.uri,
|
||||||
type: spotifyAlbum.type,
|
type: spotifyAlbum.type,
|
||||||
href: spotifyAlbum.href
|
href: spotifyAlbum.href,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,7 +196,7 @@ export class SpotifyService {
|
||||||
retryOnExpiredToken: boolean = true
|
retryOnExpiredToken: boolean = true
|
||||||
): Promise<Artist> {
|
): Promise<Artist> {
|
||||||
const artist = await this.musicLibraryService.findArtist({
|
const artist = await this.musicLibraryService.findArtist({
|
||||||
spotify: { id: spotifyID }
|
spotify: { id: spotifyID },
|
||||||
});
|
});
|
||||||
if (artist) {
|
if (artist) {
|
||||||
return artist;
|
return artist;
|
||||||
|
|
@ -225,8 +225,8 @@ export class SpotifyService {
|
||||||
id: spotifyArtist.id,
|
id: spotifyArtist.id,
|
||||||
uri: spotifyArtist.uri,
|
uri: spotifyArtist.uri,
|
||||||
type: spotifyArtist.type,
|
type: spotifyArtist.type,
|
||||||
href: spotifyArtist.href
|
href: spotifyArtist.href,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,6 @@ export class User {
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
photo?: string;
|
photo?: string;
|
||||||
|
|
||||||
@Column(type => SpotifyConnection)
|
@Column((type) => SpotifyConnection)
|
||||||
spotify: SpotifyConnection;
|
spotify: SpotifyConnection;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
import { UsersController } from './users.controller';
|
import { UsersController } from "./users.controller";
|
||||||
|
|
||||||
describe('Users Controller', () => {
|
describe("Users Controller", () => {
|
||||||
let controller: UsersController;
|
let controller: UsersController;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|
@ -12,7 +12,7 @@ describe('Users Controller', () => {
|
||||||
controller = module.get<UsersController>(UsersController);
|
controller = module.get<UsersController>(UsersController);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be defined', () => {
|
it("should be defined", () => {
|
||||||
expect(controller).toBeDefined();
|
expect(controller).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export class UsersController {
|
||||||
return {
|
return {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
displayName: user.displayName,
|
displayName: user.displayName,
|
||||||
photo: user.photo
|
photo: user.photo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ import { Module } from "@nestjs/common";
|
||||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
import { UserRepository } from "./user.repository";
|
import { UserRepository } from "./user.repository";
|
||||||
import { UsersService } from "./users.service";
|
import { UsersService } from "./users.service";
|
||||||
import { UsersController } from './users.controller';
|
import { UsersController } from "./users.controller";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([UserRepository])],
|
imports: [TypeOrmModule.forFeature([UserRepository])],
|
||||||
providers: [UsersService],
|
providers: [UsersService],
|
||||||
exports: [UsersService],
|
exports: [UsersService],
|
||||||
controllers: [UsersController]
|
controllers: [UsersController],
|
||||||
})
|
})
|
||||||
export class UsersModule {}
|
export class UsersModule {}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ describe("UsersService", () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [UsersService]
|
providers: [UsersService],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
service = module.get<UsersService>(UsersService);
|
service = module.get<UsersService>(UsersService);
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,14 @@ export class UsersService {
|
||||||
|
|
||||||
async createOrUpdate(data: CreateOrUpdateDto): Promise<User> {
|
async createOrUpdate(data: CreateOrUpdateDto): Promise<User> {
|
||||||
let user = await this.userRepository.findOne({
|
let user = await this.userRepository.findOne({
|
||||||
where: { spotify: { id: data.spotify.id } }
|
where: { spotify: { id: data.spotify.id } },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = this.userRepository.create({
|
user = this.userRepository.create({
|
||||||
spotify: {
|
spotify: {
|
||||||
id: data.spotify.id
|
id: data.spotify.id,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue