Compare commits
4 Commits
preview-fi
...
preview-wi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acc3599f1b | ||
|
|
64f4610b9f | ||
|
|
2d3b777daf | ||
|
|
cf59102ef9 |
@@ -18,7 +18,7 @@ config/logs/*
|
|||||||
config/*.json
|
config/*.json
|
||||||
dist
|
dist
|
||||||
Dockerfile*
|
Dockerfile*
|
||||||
docker-compose.yml
|
compose.yaml
|
||||||
docs
|
docs
|
||||||
LICENSE
|
LICENSE
|
||||||
node_modules
|
node_modules
|
||||||
|
|||||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -40,7 +40,7 @@ docs export-ignore
|
|||||||
.all-contributorsrc export-ignore
|
.all-contributorsrc export-ignore
|
||||||
.editorconfig export-ignore
|
.editorconfig export-ignore
|
||||||
Dockerfile.local export-ignore
|
Dockerfile.local export-ignore
|
||||||
docker-compose.yml export-ignore
|
compose.yaml export-ignore
|
||||||
stylelint.config.js export-ignore
|
stylelint.config.js export-ignore
|
||||||
|
|
||||||
public/os_logo_filled.png export-ignore
|
public/os_logo_filled.png export-ignore
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ All help is welcome and greatly appreciated! If you would like to contribute to
|
|||||||
pnpm dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
- Alternatively, you can use [Docker](https://www.docker.com/) with `docker-compose up -d`. This method does not require installing NodeJS or Yarn on your machine directly.
|
- Alternatively, you can use [Docker](https://www.docker.com/) with `docker compose up -d`. This method does not require installing NodeJS or Yarn on your machine directly.
|
||||||
|
|
||||||
5. Create your patch and test your changes.
|
5. Create your patch and test your changes.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
version: '3'
|
|
||||||
services:
|
services:
|
||||||
jellyseerr:
|
jellyseerr:
|
||||||
build:
|
build:
|
||||||
@@ -190,7 +190,7 @@ Caddy will automatically obtain and renew SSL certificates for your domain.
|
|||||||
|
|
||||||
## Traefik (v2)
|
## Traefik (v2)
|
||||||
|
|
||||||
Add the following labels to the Jellyseerr service in your `docker-compose.yml` file:
|
Add the following labels to the Jellyseerr service in your `compose.yaml` file:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
labels:
|
labels:
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ You could also use [diun](https://github.com/crazy-max/diun) to receive notifica
|
|||||||
For details on how to use Docker Compose, please [review the official Compose documentation](https://docs.docker.com/compose/reference/).
|
For details on how to use Docker Compose, please [review the official Compose documentation](https://docs.docker.com/compose/reference/).
|
||||||
|
|
||||||
#### Installation:
|
#### Installation:
|
||||||
Define the `jellyseerr` service in your `docker-compose.yml` as follows:
|
Define the `jellyseerr` service in your `compose.yaml` as follows:
|
||||||
```yaml
|
```yaml
|
||||||
---
|
---
|
||||||
services:
|
services:
|
||||||
@@ -94,17 +94,17 @@ If you are using emby, make sure to set the `JELLYFIN_TYPE` environment variable
|
|||||||
|
|
||||||
Then, start all services defined in the Compose file:
|
Then, start all services defined in the Compose file:
|
||||||
```bash
|
```bash
|
||||||
docker-compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Updating:
|
#### Updating:
|
||||||
Pull the latest image:
|
Pull the latest image:
|
||||||
```bash
|
```bash
|
||||||
docker-compose pull jellyseerr
|
docker compose pull jellyseerr
|
||||||
```
|
```
|
||||||
Then, restart all services defined in the Compose file:
|
Then, restart all services defined in the Compose file:
|
||||||
```bash
|
```bash
|
||||||
docker-compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
:::tip
|
:::tip
|
||||||
You may alternatively use a third-party mechanism like [dockge](https://github.com/louislam/dockge) to manage your docker compose files.
|
You may alternatively use a third-party mechanism like [dockge](https://github.com/louislam/dockge) to manage your docker compose files.
|
||||||
|
|||||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@@ -2,4 +2,4 @@
|
|||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { RateLimitOptions } from '@server/utils/rateLimit';
|
import type { RateLimitOptions } from '@server/utils/rateLimit';
|
||||||
import rateLimit from '@server/utils/rateLimit';
|
// import rateLimit from '@server/utils/rateLimit';
|
||||||
import type NodeCache from 'node-cache';
|
import type NodeCache from 'node-cache';
|
||||||
|
|
||||||
// 5 minute default TTL (in seconds)
|
// 5 minute default TTL (in seconds)
|
||||||
@@ -26,11 +26,12 @@ class ExternalAPI {
|
|||||||
params: Record<string, string> = {},
|
params: Record<string, string> = {},
|
||||||
options: ExternalAPIOptions = {}
|
options: ExternalAPIOptions = {}
|
||||||
) {
|
) {
|
||||||
if (options.rateLimit) {
|
// if (options.rateLimit) {
|
||||||
this.fetch = rateLimit(fetch, options.rateLimit);
|
// this.fetch = rateLimit(fetch, options.rateLimit);
|
||||||
} else {
|
// } else {
|
||||||
|
// this.fetch = fetch;
|
||||||
|
// }
|
||||||
this.fetch = fetch;
|
this.fetch = fetch;
|
||||||
}
|
|
||||||
|
|
||||||
const url = new URL(baseUrl);
|
const url = new URL(baseUrl);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import logger from '@server/logger';
|
import logger from '@server/logger';
|
||||||
import type { RateLimitOptions } from '@server/utils/rateLimit';
|
import type { RateLimitOptions } from '@server/utils/rateLimit';
|
||||||
import rateLimit from '@server/utils/rateLimit';
|
// import rateLimit from '@server/utils/rateLimit';
|
||||||
import { createHash } from 'crypto';
|
import { createHash } from 'crypto';
|
||||||
import { promises } from 'fs';
|
import { promises } from 'fs';
|
||||||
import mime from 'mime/lite';
|
import mime from 'mime/lite';
|
||||||
@@ -150,13 +150,14 @@ class ImageProxy {
|
|||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
|
|
||||||
if (options.rateLimitOptions) {
|
// if (options.rateLimitOptions) {
|
||||||
this.fetch = rateLimit(fetch, {
|
// this.fetch = rateLimit(fetch, {
|
||||||
...options.rateLimitOptions,
|
// ...options.rateLimitOptions,
|
||||||
});
|
// });
|
||||||
} else {
|
// } else {
|
||||||
|
// this.fetch = fetch;
|
||||||
|
// }
|
||||||
this.fetch = fetch;
|
this.fetch = fetch;
|
||||||
}
|
|
||||||
this.headers = options.headers || null;
|
this.headers = options.headers || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -299,14 +299,27 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
|
|||||||
where: { jellyfinUserId: account.User.Id },
|
where: { jellyfinUserId: account.User.Id },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user && !(await userRepository.count())) {
|
const missingAdminUser = !user && !(await userRepository.count());
|
||||||
|
if (
|
||||||
|
missingAdminUser ||
|
||||||
|
settings.main.mediaServerType === MediaServerType.NOT_CONFIGURED
|
||||||
|
) {
|
||||||
// Check if user is admin on jellyfin
|
// Check if user is admin on jellyfin
|
||||||
if (account.User.Policy.IsAdministrator === false) {
|
if (account.User.Policy.IsAdministrator === false) {
|
||||||
throw new ApiError(403, ApiErrorCode.NotAdmin);
|
throw new ApiError(403, ApiErrorCode.NotAdmin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
body.serverType !== MediaServerType.JELLYFIN &&
|
||||||
|
body.serverType !== MediaServerType.EMBY
|
||||||
|
) {
|
||||||
|
throw new Error('select_server_type');
|
||||||
|
}
|
||||||
|
settings.main.mediaServerType = body.serverType;
|
||||||
|
|
||||||
|
if (missingAdminUser) {
|
||||||
logger.info(
|
logger.info(
|
||||||
'Sign-in attempt from Jellyfin user with access to the media server; creating initial admin user for Overseerr',
|
'Sign-in attempt from Jellyfin user with access to the media server; creating initial admin user for Jellyseerr',
|
||||||
{
|
{
|
||||||
label: 'API',
|
label: 'API',
|
||||||
ip: req.ip,
|
ip: req.ip,
|
||||||
@@ -316,10 +329,9 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
|
|||||||
|
|
||||||
// User doesn't exist, and there are no users in the database, we'll create the user
|
// User doesn't exist, and there are no users in the database, we'll create the user
|
||||||
// with admin permissions
|
// with admin permissions
|
||||||
switch (body.serverType) {
|
|
||||||
case MediaServerType.EMBY:
|
|
||||||
settings.main.mediaServerType = MediaServerType.EMBY;
|
|
||||||
user = new User({
|
user = new User({
|
||||||
|
id: 1,
|
||||||
email: body.email || account.User.Name,
|
email: body.email || account.User.Name,
|
||||||
jellyfinUsername: account.User.Name,
|
jellyfinUsername: account.User.Name,
|
||||||
jellyfinUserId: account.User.Id,
|
jellyfinUserId: account.User.Id,
|
||||||
@@ -327,26 +339,44 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
|
|||||||
jellyfinAuthToken: account.AccessToken,
|
jellyfinAuthToken: account.AccessToken,
|
||||||
permissions: Permission.ADMIN,
|
permissions: Permission.ADMIN,
|
||||||
avatar: `/avatarproxy/${account.User.Id}`,
|
avatar: `/avatarproxy/${account.User.Id}`,
|
||||||
userType: UserType.EMBY,
|
userType:
|
||||||
|
body.serverType === MediaServerType.JELLYFIN
|
||||||
|
? UserType.JELLYFIN
|
||||||
|
: UserType.EMBY,
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
await userRepository.save(user);
|
||||||
case MediaServerType.JELLYFIN:
|
} else {
|
||||||
settings.main.mediaServerType = MediaServerType.JELLYFIN;
|
logger.info(
|
||||||
user = new User({
|
'Sign-in attempt from Jellyfin user with access to the media server; editing admin user for Jellyseerr',
|
||||||
email: body.email || account.User.Name,
|
{
|
||||||
|
label: 'API',
|
||||||
|
ip: req.ip,
|
||||||
jellyfinUsername: account.User.Name,
|
jellyfinUsername: account.User.Name,
|
||||||
jellyfinUserId: account.User.Id,
|
}
|
||||||
jellyfinDeviceId: deviceId,
|
);
|
||||||
jellyfinAuthToken: account.AccessToken,
|
|
||||||
permissions: Permission.ADMIN,
|
|
||||||
avatar: `/avatarproxy/${account.User.Id}`,
|
|
||||||
userType: UserType.JELLYFIN,
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
// User alread exist but settings.json is not configured, we'll edit the admin user
|
||||||
default:
|
|
||||||
throw new Error('select_server_type');
|
user = await userRepository.findOne({
|
||||||
|
where: { id: 1 },
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('Unable to find admin user to edit');
|
||||||
|
}
|
||||||
|
user.email = body.email || account.User.Name;
|
||||||
|
user.jellyfinUsername = account.User.Name;
|
||||||
|
user.jellyfinUserId = account.User.Id;
|
||||||
|
user.jellyfinDeviceId = deviceId;
|
||||||
|
user.jellyfinAuthToken = account.AccessToken;
|
||||||
|
user.permissions = Permission.ADMIN;
|
||||||
|
user.avatar = `/avatarproxy/${account.User.Id}`;
|
||||||
|
user.userType =
|
||||||
|
body.serverType === MediaServerType.JELLYFIN
|
||||||
|
? UserType.JELLYFIN
|
||||||
|
: UserType.EMBY;
|
||||||
|
|
||||||
|
await userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an API key on Jellyfin from this admin user
|
// Create an API key on Jellyfin from this admin user
|
||||||
@@ -368,8 +398,6 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
|
|||||||
settings.jellyfin.apiKey = apiKey;
|
settings.jellyfin.apiKey = apiKey;
|
||||||
await settings.save();
|
await settings.save();
|
||||||
startJobs();
|
startJobs();
|
||||||
|
|
||||||
await userRepository.save(user);
|
|
||||||
}
|
}
|
||||||
// User already exists, let's update their information
|
// User already exists, let's update their information
|
||||||
else if (account.User.Id === user?.jellyfinUserId) {
|
else if (account.User.Id === user?.jellyfinUserId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user