Skip to content

Commit

Permalink
feat: fixing merge conflicts, adding user linking for emby connect
Browse files Browse the repository at this point in the history
  • Loading branch information
pXius committed Feb 22, 2025
1 parent 844bfe6 commit d1cec17
Show file tree
Hide file tree
Showing 7 changed files with 472 additions and 1,458 deletions.
1,717 changes: 339 additions & 1,378 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion server/api/embyconnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class EmbyConnectAPI extends ExternalAPI {
return {
User: {
Name: connectAuthResponse.User.Name,
Email: connectAuthResponse.User.Email,
ServerId: matchingServer.SystemId,
ServerName: matchingServer.Name,
Id: localUserExchangeResponse.LocalUserId,
Expand Down Expand Up @@ -122,7 +123,10 @@ class EmbyConnectAPI extends ExternalAPI {
label: 'EmbyConnect API',
ip: this.ClientIP,
});
throw new ApiError(e.cause?.status, ApiErrorCode.InvalidCredentials);
throw new ApiError(
e.cause?.status ?? 401,
ApiErrorCode.InvalidCredentials
);
}
}

Expand Down
9 changes: 4 additions & 5 deletions server/api/externalapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ class ExternalAPI {
ttl?: number,
config?: RequestInit
): Promise<T> {
const headers = { ...this.defaultHeaders, ...config?.headers };
const headers = new Headers({
...this.defaultHeaders,
...(config?.headers || {}),
});
const cacheKey = this.serializeCacheKey(endpoint, {
config: { ...this.params, ...params },
headers,
Expand All @@ -125,10 +128,6 @@ class ExternalAPI {
}

const url = this.formatUrl(endpoint, params);
const headers = new Headers({
...this.defaultHeaders,
...(config?.headers || {}),
});

const isFormUrlEncoded = headers
.get('Content-Type')
Expand Down
75 changes: 40 additions & 35 deletions server/api/jellyfin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as EmailValidator from 'email-validator';

export interface JellyfinUserResponse {
Name: string;
Email?: string;
ServerId: string;
ServerName: string;
Id: string;
Expand Down Expand Up @@ -141,6 +142,31 @@ class JellyfinAPI extends ExternalAPI {
);
};

if (
getSettings().main.mediaServerType === MediaServerType.EMBY &&
Username &&
EmailValidator.validate(Username)
) {
try {
const connectApi = new EmbyConnectAPI({
ClientIP: ClientIP,
DeviceId: this.deviceId,
});
return await connectApi.authenticateConnectUser(Username, Password);
} catch (e) {
// Possible local Emby user with email as username
logger.warn(
`Emby Connect authentication failed: ${e}, attempting local Emby server authentication`,
{
label: 'Jellyfin API',
error:
e.cause?.message ?? e.cause?.statusText ?? ApiErrorCode.Unknown,
ip: ClientIP,
}
);
}
}

try {
return await authenticate(true);
} catch (e) {
Expand All @@ -164,39 +190,18 @@ class JellyfinAPI extends ExternalAPI {
} catch (e) {
if (e.cause.status === 401) {
throw new ApiError(e.cause.status, ApiErrorCode.InvalidCredentials);
} else {
logger.error(
'Something went wrong while authenticating with the Jellyfin server',
{
label: 'Jellyfin API',
error: e.cause.message ?? e.cause.statusText,
ip: ClientIP,
}
);
throw new ApiError(e.cause.status, ApiErrorCode.Unknown);
}
}

const settings = getSettings();

if (
settings.main.mediaServerType === MediaServerType.EMBY &&
Username &&
EmailValidator.validate(Username)
) {
try {
const connectApi = new EmbyConnectAPI({
ClientIP: ClientIP,
DeviceId: this.deviceId,
});

return await connectApi.authenticateConnectUser(Username, Password);
} catch (e) {
logger.debug(`Emby Connect authentication failed: ${e}`);
throw new ApiError(e.cause?.status, ApiErrorCode.InvalidCredentials);
}
} else {
logger.error(
'Something went wrong while authenticating with the Jellyfin server',
{
label: 'Jellyfin API',
error: e.cause.message ?? e.cause.statusText,
ip: ClientIP,
}
);

throw new ApiError(e.cause.status, ApiErrorCode.Unknown);
}
}

public setUserId(userId: string): void {
Expand Down Expand Up @@ -233,9 +238,9 @@ class JellyfinAPI extends ExternalAPI {

public async getUsers(): Promise<JellyfinUserListResponse> {
try {
const userReponse = await this.get<JellyfinUserResponse[]>(`/Users`);
const userResponse = await this.get<JellyfinUserResponse[]>(`/Users`);

return { users: userReponse };
return { users: userResponse };
} catch (e) {
logger.error(
'Something went wrong while getting the account from the Jellyfin server',
Expand All @@ -248,10 +253,10 @@ class JellyfinAPI extends ExternalAPI {

public async getUser(): Promise<JellyfinUserResponse> {
try {
const userReponse = await this.get<JellyfinUserResponse>(
const userResponse = await this.get<JellyfinUserResponse>(
`/Users/${this.userId ?? 'Me'}`
);
return userReponse;
return userResponse;
} catch (e) {
logger.error(
'Something went wrong while getting the account from the Jellyfin server',
Expand Down
104 changes: 68 additions & 36 deletions server/routes/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ authRoutes.get('/me', isAuthenticated(), async (req, res) => {
user.warnings.push('userEmailRequired');
logger.warn(`User ${user.username} has no valid email address`);
}

return res.status(200).json(user);
});

Expand Down Expand Up @@ -402,25 +401,34 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
}
// User already exists, let's update their information
else if (account.User.Id === user?.jellyfinUserId) {
const serverType =
settings.main.mediaServerType === MediaServerType.JELLYFIN
? ServerType.JELLYFIN
: ServerType.EMBY;

const userType =
serverType === ServerType.JELLYFIN ? UserType.JELLYFIN : UserType.EMBY;

logger.info(
`Found matching ${
settings.main.mediaServerType === MediaServerType.JELLYFIN
? ServerType.JELLYFIN
: ServerType.EMBY
} user; updating user with ${
settings.main.mediaServerType === MediaServerType.JELLYFIN
? ServerType.JELLYFIN
: ServerType.EMBY
}`,
`Found matching ${serverType} user; updating user with ${userType}`,
{
label: 'API',
ip: req.ip,
jellyfinUsername: account.User.Name,
}
);

user.userType = userType;
user.avatar = `/avatarproxy/${account.User.Id}`;
user.jellyfinUsername = account.User.Name;

if (
account.User.Email !== undefined &&
user.email !== account.User.Email
) {
user.email = account.User.Email;
}

if (user.username === account.User.Name) {
user.username = '';
}
Expand All @@ -441,36 +449,60 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
message: 'Access denied.',
});
} else if (!user) {
logger.info(
'Sign-in attempt from Jellyfin user with access to the media server; creating new Overseerr user',
{
label: 'API',
ip: req.ip,
jellyfinUsername: account.User.Name,
}
);
// Emby Connect user with unlinked local account?
if (
settings.main.mediaServerType === MediaServerType.EMBY &&
account.User.Email &&
account.User.Email.trim() !== ''
) {
user = await userRepository.findOne({
where: { email: account.User.Email },
});
}

user = new User({
email: body.email,
jellyfinUsername: account.User.Name,
jellyfinUserId: account.User.Id,
jellyfinDeviceId: deviceId,
permissions: settings.main.defaultPermissions,
avatar: `/avatarproxy/${account.User.Id}`,
userType:
settings.main.mediaServerType === MediaServerType.JELLYFIN
? UserType.JELLYFIN
: UserType.EMBY,
});
if (user) {
logger.info(
`Sign in attempt from EmbyConnect user with access to the media server, linking users`
);
user.avatar = `/avatarproxy/${account.User.Id}`;
user.jellyfinUserId = account.User.Id;
user.userType = UserType.EMBY;
user.username = account.User.Name;
await userRepository.save(user);

// No user, create new
} else {
logger.info(
'Sign-in attempt from Jellyfin/Emby user with access to the media server; creating new Jellyseerr user',
{
label: 'API',
ip: req.ip,
jellyfinUsername: account.User.Name,
}
);

user = new User({
email: body.email,
jellyfinUsername: account.User.Name,
jellyfinUserId: account.User.Id,
jellyfinDeviceId: deviceId,
permissions: settings.main.defaultPermissions,
avatar: `/avatarproxy/${account.User.Id}`,
userType:
settings.main.mediaServerType === MediaServerType.JELLYFIN
? UserType.JELLYFIN
: UserType.EMBY,
});

//initialize Jellyfin/Emby users with local login
const passedExplicitPassword = body.password && body.password.length > 0;
if (passedExplicitPassword) {
await user.setPassword(body.password ?? '');
//initialize Jellyfin/Emby users with local login
const passedExplicitPassword =
body.password && body.password.length > 0;
if (passedExplicitPassword) {
await user.setPassword(body.password ?? '');
}
await userRepository.save(user);
}
await userRepository.save(user);
}

// Set logged in session
if (req.session) {
req.session.userId = user?.id;
Expand Down
14 changes: 12 additions & 2 deletions src/components/Login/JellyfinLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -437,15 +437,25 @@ const JellyfinLogin: React.FC<JellyfinLoginProps> = ({
<Form>
<div className="sm:border-t sm:border-gray-800">
<label htmlFor="username" className="text-label">
{intl.formatMessage(messages.username)}
{serverType === MediaServerType.EMBY
? `Emby Connect ${intl.formatMessage(
messages.email
)} / ${intl.formatMessage(messages.username)}`
: intl.formatMessage(messages.username)}
</label>
<div className="mt-1 mb-2 sm:col-span-2 sm:mt-0">
<div className="flex max-w-lg rounded-md shadow-sm">
<Field
id="username"
name="username"
type="text"
placeholder={intl.formatMessage(messages.username)}
placeholder={
serverType === MediaServerType.EMBY
? `Emby Connect ${intl.formatMessage(
messages.email
)} / ${intl.formatMessage(messages.username)}`
: intl.formatMessage(messages.username)
}
/>
</div>
{errors.username && touched.username && (
Expand Down
5 changes: 4 additions & 1 deletion src/components/Login/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,10 @@ const Login = () => {
onAuthToken={(authToken) => setAuthToken(authToken)}
/>
) : (
<JellyfinLogin revalidate={revalidate} />
<JellyfinLogin
revalidate={revalidate}
serverType={settings.currentSettings.mediaServerType}
/>
)}
</div>
</AccordionContent>
Expand Down

0 comments on commit d1cec17

Please sign in to comment.