diff --git a/backend/package-lock.json b/backend/package-lock.json index db492af..77abd70 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -14,6 +14,7 @@ "@nestjs/jwt": "^10.2.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/serve-static": "^4.0.1", + "@nestjs/swagger": "^7.3.0", "reflect-metadata": "^0.2.1", "rxjs": "^7.8.1", "sqlite3": "^5.1.7", @@ -1604,6 +1605,11 @@ "node": ">=8" } }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==" + }, "node_modules/@nestjs/cli": { "version": "10.3.2", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.3.2.tgz", @@ -1811,6 +1817,25 @@ "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0" } }, + "node_modules/@nestjs/mapped-types": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", + "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/platform-express": { "version": "10.3.5", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.3.5.tgz", @@ -1884,6 +1909,38 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.2.5.tgz", "integrity": "sha512-l6qtdDPIkmAmzEO6egquYDfqQGPMRNGjYtrU13HAXb3YSRrt7HSb1sJY0pKp6o2bAa86tSB6iwaW2JbthPKr7Q==" }, + "node_modules/@nestjs/swagger": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.3.0.tgz", + "integrity": "sha512-zLkfKZ+ioYsIZ3dfv7Bj8YHnZMNAGWFUmx2ZDuLp/fBE4P8BSjB7hldzDueFXsmwaPL90v7lgyd82P+s7KME1Q==", + "dependencies": { + "@microsoft/tsdoc": "^0.14.2", + "@nestjs/mapped-types": "2.0.5", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "path-to-regexp": "3.2.0", + "swagger-ui-dist": "5.11.2" + }, + "peerDependencies": { + "@fastify/static": "^6.0.0 || ^7.0.0", + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/testing": { "version": "10.3.5", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.3.5.tgz", @@ -3043,8 +3100,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-flatten": { "version": "1.1.1", @@ -6754,7 +6810,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -6947,8 +7002,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.includes": { "version": "4.3.0", @@ -9361,6 +9415,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "5.11.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.2.tgz", + "integrity": "sha512-jQG0cRgJNMZ7aCoiFofnoojeSaa/+KgWaDlfgs8QN+BXoGMpxeMVY5OEnjq4OlNvF3yjftO8c9GRAgcHlO+u7A==" + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/backend/package.json b/backend/package.json index 0f20da1..f729698 100644 --- a/backend/package.json +++ b/backend/package.json @@ -25,6 +25,7 @@ "@nestjs/jwt": "^10.2.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/serve-static": "^4.0.1", + "@nestjs/swagger": "^7.3.0", "reflect-metadata": "^0.2.1", "rxjs": "^7.8.1", "sqlite3": "^5.1.7", diff --git a/backend/src/app.controller.spec.ts b/backend/src/app.controller.spec.ts deleted file mode 100644 index d22f389..0000000 --- a/backend/src/app.controller.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; - -describe('AppController', () => { - let appController: AppController; - - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); - - appController = app.get(AppController); - }); - - describe('root', () => { - it('should return "Hello World!"', () => { - expect(appController.getHello()).toBe('Hello World!'); - }); - }); -}); diff --git a/backend/src/auth/auth.controller.spec.ts b/backend/src/auth/auth.controller.spec.ts deleted file mode 100644 index 27a31e6..0000000 --- a/backend/src/auth/auth.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthController } from './auth.controller'; - -describe('AuthController', () => { - let controller: AuthController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [AuthController], - }).compile(); - - controller = module.get(AuthController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/backend/src/auth/auth.guard.spec.ts b/backend/src/auth/auth.guard.spec.ts deleted file mode 100644 index e4934ab..0000000 --- a/backend/src/auth/auth.guard.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { AuthGuard } from './auth.guard'; - -describe('AuthGuard', () => { - it('should be defined', () => { - expect(new AuthGuard()).toBeDefined(); - }); -}); diff --git a/backend/src/auth/auth.guard.ts b/backend/src/auth/auth.guard.ts index 0e92220..b6f6ed3 100644 --- a/backend/src/auth/auth.guard.ts +++ b/backend/src/auth/auth.guard.ts @@ -1,16 +1,26 @@ import { CanActivate, + createParamDecorator, ExecutionContext, Injectable, UnauthorizedException, } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { JWT_SECRET } from '../consts'; -import { AuthUser } from './auth.service'; +import { AccessTokenPayload } from './auth.service'; +import { User } from '../user/user.entity'; +import { UserService } from '../user/user.service'; + +export const GetUser = createParamDecorator((data, req): User => { + return req.user; +}); @Injectable() export class AuthGuard implements CanActivate { - constructor(private jwtService: JwtService) {} + constructor( + private jwtService: JwtService, + private userService: UserService, + ) {} async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); @@ -19,12 +29,15 @@ export class AuthGuard implements CanActivate { throw new UnauthorizedException(); } try { - const payload = await this.jwtService.verifyAsync(token, { - secret: JWT_SECRET, - }); - // 💡 We're assigning the payload to the request object here - // so that we can access it in our route handlers - request['user'] = payload as AuthUser; + const payload: AccessTokenPayload = await this.jwtService.verifyAsync( + token, + { + secret: JWT_SECRET, + }, + ); + if (payload.sub) { + request['user'] = await this.userService.findOne(payload.sub); + } } catch { throw new UnauthorizedException(); } diff --git a/backend/src/auth/auth.service.spec.ts b/backend/src/auth/auth.service.spec.ts deleted file mode 100644 index 800ab66..0000000 --- a/backend/src/auth/auth.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthService } from './auth.service'; - -describe('AuthService', () => { - let service: AuthService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [AuthService], - }).compile(); - - service = module.get(AuthService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts index 5fb06e9..84340c0 100644 --- a/backend/src/auth/auth.service.ts +++ b/backend/src/auth/auth.service.ts @@ -2,10 +2,8 @@ import { Injectable, UnauthorizedException } from '@nestjs/common'; import { UserService } from '../user/user.service'; import { JwtService } from '@nestjs/jwt'; -interface AccessTokenPayload { +export interface AccessTokenPayload { sub: string; - name: string; - isAdmin: boolean; } @Injectable() @@ -29,8 +27,6 @@ export class AuthService { const payload: AccessTokenPayload = { sub: user.id, - name: user.name, - isAdmin: user.isAdmin, }; return { @@ -38,8 +34,3 @@ export class AuthService { }; } } -export type AuthUser = { - id: string; - name: string; - isAdmin: boolean; -}; diff --git a/backend/src/generate-openapi.ts b/backend/src/generate-openapi.ts new file mode 100644 index 0000000..e41c7c2 --- /dev/null +++ b/backend/src/generate-openapi.ts @@ -0,0 +1,19 @@ +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; +import 'reflect-metadata'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import * as fs from 'fs'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + app.setGlobalPrefix('api'); + + const config = new DocumentBuilder().build(); + + const document = SwaggerModule.createDocument(app, config, { + deepScanRoutes: true, + }); + SwaggerModule.setup('openapi', app, document); + fs.writeFileSync('./swagger-spec.json', JSON.stringify(document)); +} +bootstrap(); diff --git a/backend/src/main.ts b/backend/src/main.ts index 678c6c7..3a0af81 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -1,10 +1,21 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import 'reflect-metadata'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import * as fs from 'fs'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.setGlobalPrefix('api'); + + const config = new DocumentBuilder().build(); + + const document = SwaggerModule.createDocument(app, config, { + deepScanRoutes: true, + }); + SwaggerModule.setup('openapi', app, document); + fs.writeFileSync('./swagger-spec.json', JSON.stringify(document)); + await app.listen(3000); } bootstrap(); diff --git a/backend/src/user/user.controller.spec.ts b/backend/src/user/user.controller.spec.ts deleted file mode 100644 index 7057a1a..0000000 --- a/backend/src/user/user.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { UserController } from './user.controller'; - -describe('UserController', () => { - let controller: UserController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [UserController], - }).compile(); - - controller = module.get(UserController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/backend/src/user/user.controller.ts b/backend/src/user/user.controller.ts index f878561..26b9e12 100644 --- a/backend/src/user/user.controller.ts +++ b/backend/src/user/user.controller.ts @@ -8,55 +8,65 @@ import { Param, Post, UseGuards, - Request, } from '@nestjs/common'; import { UserService } from './user.service'; -import { AuthGuard } from '../auth/auth.guard'; -import { AuthUser } from 'src/auth/auth.service'; +import { AuthGuard, GetUser } from '../auth/auth.guard'; +import { ApiNotFoundResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger'; +import { CreateUserDto, UserDto } from './user.dto'; +import { User } from './user.entity'; +@ApiTags('user') @Controller('user') export class UserController { constructor(private userService: UserService) {} @UseGuards(AuthGuard) @Get() - async getProfile(@Request() req) { - const user = await this.userService.findOne((req.user as AuthUser).id); - + @ApiNotFoundResponse({ description: 'User not found' }) + @ApiOkResponse({ description: 'User found', type: UserDto }) + async getProfile(@GetUser() user: User): Promise { if (!user) { throw new NotFoundException(); } - return user; + return UserDto.fromEntity(user); } + @UseGuards(AuthGuard) @Get(':id') - async findById(@Param('id') id: string) { + @ApiOkResponse({ description: 'User found', type: UserDto }) + @ApiNotFoundResponse({ description: 'User not found' }) + async findById( + @Param('id') id: string, + @GetUser() callerUser: User, + ): Promise { + if (!callerUser.isAdmin && callerUser.id !== id) { + throw new NotFoundException(); + } + const user = await this.userService.findOne(id); if (!user) { throw new NotFoundException(); } - return user; + return UserDto.fromEntity(user); } @HttpCode(HttpStatus.OK) @Post() async create( @Body() - userCreateDto: { - name: string; - password: string; - isAdmin?: boolean; - }, + userCreateDto: CreateUserDto, ) { const canCreateAdmin = await this.userService.noPreviousAdmins(); - return this.userService.create( + const user = await this.userService.create( userCreateDto.name, userCreateDto.password, canCreateAdmin && userCreateDto.isAdmin, ); + + return UserDto.fromEntity(user); } } diff --git a/backend/src/user/user.dto.ts b/backend/src/user/user.dto.ts new file mode 100644 index 0000000..48344f8 --- /dev/null +++ b/backend/src/user/user.dto.ts @@ -0,0 +1,21 @@ +import { OmitType, PickType } from '@nestjs/swagger'; +import { User } from './user.entity'; + +export class UserDto extends OmitType(User, ['password'] as const) { + static fromEntity(entity: User): UserDto { + return { + id: entity.id, + name: entity.name, + isAdmin: entity.isAdmin, + settings: entity.settings, + }; + } +} + +export class CreateUserDto extends PickType(User, [ + 'name', + 'password', + 'isAdmin', +] as const) {} + +export class UpdateUserDto extends OmitType(User, ['id'] as const) {} diff --git a/backend/src/user/user.entity.ts b/backend/src/user/user.entity.ts index 7168a37..3197892 100644 --- a/backend/src/user/user.entity.ts +++ b/backend/src/user/user.entity.ts @@ -1,6 +1,60 @@ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { ApiProperty } from '@nestjs/swagger'; -const DEFAULT_SETTINGS = { +export class SonarrSettings { + @ApiProperty({ required: true }) + apiKey: string; + @ApiProperty({ required: true }) + baseUrl: string; + @ApiProperty({ required: true }) + qualityProfileId: number; + @ApiProperty({ required: true }) + rootFolderPath: string; + @ApiProperty({ required: true }) + languageProfileId: number; +} + +export class RadarrSettings { + @ApiProperty({ required: true }) + apiKey: string; + @ApiProperty({ required: true }) + baseUrl: string; + @ApiProperty({ required: true }) + qualityProfileId: number; + @ApiProperty({ required: true }) + rootFolderPath: string; +} + +export class JellyfinSettings { + @ApiProperty({ required: true }) + apiKey: string; + @ApiProperty({ required: true }) + baseUrl: string; + @ApiProperty({ required: true }) + userId: string; +} + +export class Settings { + @ApiProperty({ required: true }) + autoplayTrailers: boolean; + @ApiProperty({ required: true }) + language: string; + @ApiProperty({ required: true }) + animationDuration: number; + // discover: { + // region: string, + // excludeLibraryItems: true, + // includedLanguages: 'en' + // }, + @ApiProperty({ required: true, type: SonarrSettings }) + sonarr: SonarrSettings; + @ApiProperty({ required: true, type: RadarrSettings }) + radarr: RadarrSettings; + @ApiProperty({ required: true, type: JellyfinSettings }) + jellyfin: JellyfinSettings; +} + +const DEFAULT_SETTINGS: Settings = { autoplayTrailers: true, language: 'en', animationDuration: 300, @@ -31,45 +85,23 @@ const DEFAULT_SETTINGS = { @Entity() export class User { + @ApiProperty({ required: true }) @PrimaryGeneratedColumn('uuid') id: string; + @ApiProperty({ required: true }) @Column({ unique: true }) name: string; + @ApiProperty({ required: true }) @Column() password: string; + @ApiProperty({ required: true }) @Column() isAdmin: boolean = false; + @ApiProperty({ required: true, type: Settings }) @Column('json', { default: JSON.stringify(DEFAULT_SETTINGS) }) - settings: { - autoplayTrailers: boolean; - language: string; - animationDuration: number; - // discover: { - // region: string, - // excludeLibraryItems: true, - // includedLanguages: 'en' - // }, - sonarr: { - apiKey: string; - baseUrl: string; - qualityProfileId: number; - rootFolderPath: string; - languageProfileId: number; - }; - radarr: { - apiKey: string; - baseUrl: string; - qualityProfileId: number; - rootFolderPath: string; - }; - jellyfin: { - apiKey: string; - baseUrl: string; - userId: string; - }; - } = DEFAULT_SETTINGS; + settings = DEFAULT_SETTINGS; } diff --git a/backend/src/user/user.service.spec.ts b/backend/src/user/user.service.spec.ts deleted file mode 100644 index 873de8a..0000000 --- a/backend/src/user/user.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { UserService } from './user.service'; - -describe('UserService', () => { - let service: UserService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [UserService], - }).compile(); - - service = module.get(UserService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/backend/swagger-spec.json b/backend/swagger-spec.json new file mode 100644 index 0000000..44b27fb --- /dev/null +++ b/backend/swagger-spec.json @@ -0,0 +1 @@ +{"openapi":"3.0.0","paths":{"/api/user":{"get":{"operationId":"UserController_getProfile","parameters":[],"responses":{"200":{"description":"User found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDto"}}}},"404":{"description":"User not found"}},"tags":["user"]},"post":{"operationId":"UserController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserDto"}}}},"responses":{"200":{"description":""}},"tags":["user"]}},"/api/user/{id}":{"get":{"operationId":"UserController_findById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"User found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDto"}}}},"404":{"description":"User not found"}},"tags":["user"]}},"/api/auth":{"post":{"operationId":"AuthController_signIn","parameters":[],"responses":{"200":{"description":""}}}},"/api/auth/profile":{"get":{"operationId":"AuthController_getProfile","parameters":[],"responses":{"200":{"description":""}}}},"/api":{"get":{"operationId":"AppController_getHello","parameters":[],"responses":{"200":{"description":""}}}}},"info":{"title":"","description":"","version":"1.0.0","contact":{}},"tags":[],"servers":[],"components":{"schemas":{"SonarrSettings":{"type":"object","properties":{"apiKey":{"type":"string"},"baseUrl":{"type":"string"},"qualityProfileId":{"type":"number"},"rootFolderPath":{"type":"string"},"languageProfileId":{"type":"number"}},"required":["apiKey","baseUrl","qualityProfileId","rootFolderPath","languageProfileId"]},"RadarrSettings":{"type":"object","properties":{"apiKey":{"type":"string"},"baseUrl":{"type":"string"},"qualityProfileId":{"type":"number"},"rootFolderPath":{"type":"string"}},"required":["apiKey","baseUrl","qualityProfileId","rootFolderPath"]},"JellyfinSettings":{"type":"object","properties":{"apiKey":{"type":"string"},"baseUrl":{"type":"string"},"userId":{"type":"string"}},"required":["apiKey","baseUrl","userId"]},"Settings":{"type":"object","properties":{"autoplayTrailers":{"type":"boolean"},"language":{"type":"string"},"animationDuration":{"type":"number"},"sonarr":{"$ref":"#/components/schemas/SonarrSettings"},"radarr":{"$ref":"#/components/schemas/RadarrSettings"},"jellyfin":{"$ref":"#/components/schemas/JellyfinSettings"}},"required":["autoplayTrailers","language","animationDuration","sonarr","radarr","jellyfin"]},"UserDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"isAdmin":{"type":"boolean"},"settings":{"$ref":"#/components/schemas/Settings"}},"required":["id","name","isAdmin","settings"]},"CreateUserDto":{"type":"object","properties":{"name":{"type":"string"},"password":{"type":"string"},"isAdmin":{"type":"boolean"}},"required":["name","password","isAdmin"]}}}} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index efa8ffb..e04e314 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "eslint-plugin-svelte": "^2.35.1", "hls.js": "^1.4.14", "openapi-fetch": "^0.8.2", - "openapi-typescript": "^6.7.3", + "openapi-typescript": "^6.7.5", "postcss": "^8.4.35", "prettier": "^2.8.0", "prettier-plugin-svelte": "^2.10.1", @@ -4877,9 +4877,9 @@ } }, "node_modules/openapi-typescript": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-6.7.3.tgz", - "integrity": "sha512-es3mGcDXV6TKPo6n3aohzHm0qxhLyR39MhF6mkD1FwFGjhxnqMqfSIgM0eCpInZvqatve4CxmXcMZw3jnnsaXw==", + "version": "6.7.5", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-6.7.5.tgz", + "integrity": "sha512-ZD6dgSZi0u1QCP55g8/2yS5hNJfIpgqsSGHLxxdOjvY7eIrXzj271FJEQw33VwsZ6RCtO/NOuhxa7GBWmEudyA==", "dev": true, "dependencies": { "ansi-colors": "^4.1.3", diff --git a/package.json b/package.json index ce1c3e4..7770910 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "eslint-plugin-svelte": "^2.35.1", "hls.js": "^1.4.14", "openapi-fetch": "^0.8.2", - "openapi-typescript": "^6.7.3", + "openapi-typescript": "^6.7.5", "postcss": "^8.4.35", "prettier": "^2.8.0", "prettier-plugin-svelte": "^2.10.1", diff --git a/src/lib/apis/reiverr/reiverr.generated.d.ts b/src/lib/apis/reiverr/reiverr.generated.d.ts new file mode 100644 index 0000000..1bc2890 --- /dev/null +++ b/src/lib/apis/reiverr/reiverr.generated.d.ts @@ -0,0 +1,137 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + + +export interface paths { + "/api/user": { + get: operations["UserController_getProfile"]; + post: operations["UserController_create"]; + }; + "/api/user/{id}": { + get: operations["UserController_findById"]; + }; + "/api/auth": { + post: operations["AuthController_signIn"]; + }; + "/api/auth/profile": { + get: operations["AuthController_getProfile"]; + }; + "/api": { + get: operations["AppController_getHello"]; + }; +} + +export type webhooks = Record; + +export interface components { + schemas: { + SonarrSettings: { + apiKey: string; + baseUrl: string; + qualityProfileId: number; + rootFolderPath: string; + languageProfileId: number; + }; + RadarrSettings: { + apiKey: string; + baseUrl: string; + qualityProfileId: number; + rootFolderPath: string; + }; + JellyfinSettings: { + apiKey: string; + baseUrl: string; + userId: string; + }; + Settings: { + autoplayTrailers: boolean; + language: string; + animationDuration: number; + sonarr: components["schemas"]["SonarrSettings"]; + radarr: components["schemas"]["RadarrSettings"]; + jellyfin: components["schemas"]["JellyfinSettings"]; + }; + UserDto: { + id: string; + name: string; + isAdmin: boolean; + settings: components["schemas"]["Settings"]; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} + +export type $defs = Record; + +export type external = Record; + +export interface operations { + + UserController_getProfile: { + responses: { + /** @description User found */ + 200: { + content: { + "application/json": components["schemas"]["UserDto"]; + }; + }; + /** @description User not found */ + 404: { + content: never; + }; + }; + }; + UserController_create: { + responses: { + 200: { + content: never; + }; + }; + }; + UserController_findById: { + parameters: { + path: { + id: string; + }; + }; + responses: { + /** @description User found */ + 200: { + content: { + "application/json": components["schemas"]["UserDto"]; + }; + }; + /** @description User not found */ + 404: { + content: never; + }; + }; + }; + AuthController_signIn: { + responses: { + 200: { + content: never; + }; + }; + }; + AuthController_getProfile: { + responses: { + 200: { + content: never; + }; + }; + }; + AppController_getHello: { + responses: { + 200: { + content: never; + }; + }; + }; +} diff --git a/src/lib/apis/reiverr/reiverr.openapi.json b/src/lib/apis/reiverr/reiverr.openapi.json new file mode 100644 index 0000000..e69de29 diff --git a/src/main.ts b/src/main.ts index 8a909a1..55f77a5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,10 @@ -import './app.css' -import App from './App.svelte' +import './app.css'; +import App from './App.svelte'; const app = new App({ - target: document.getElementById('app'), -}) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + target: document.getElementById('app') +}); -export default app +export default app; diff --git a/tsconfig.json b/tsconfig.json index 6507d24..a06205a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,8 @@ "allowJs": true, "checkJs": true, "isolatedModules": true, -// "paths": { + "noUncheckedIndexedAccess": true + // "paths": { // "$lib/*": ["src/lib/*"], // } },