mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-14 20:40:21 +02:00
support secondary public server URL
This commit is contained in:
@@ -101,6 +101,8 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
|
||||
(localSettings ? localSettings.env.SERVER_NAME : window.SERVER_NAME) || 'My Server',
|
||||
password: '',
|
||||
preferInstantMix: undefined,
|
||||
preferRemoteUrl: false,
|
||||
remoteUrl: '',
|
||||
savePassword: undefined,
|
||||
type:
|
||||
(localSettings
|
||||
@@ -166,6 +168,14 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
|
||||
serverItem.savePassword = values.savePassword;
|
||||
}
|
||||
|
||||
if (values.remoteUrl?.trim()) {
|
||||
serverItem.remoteUrl = values.remoteUrl.trim().replace(/\/$/, '');
|
||||
}
|
||||
|
||||
if (values.preferRemoteUrl !== undefined) {
|
||||
serverItem.preferRemoteUrl = values.preferRemoteUrl;
|
||||
}
|
||||
|
||||
if (data.ndCredential !== undefined) {
|
||||
serverItem.ndCredential = data.ndCredential;
|
||||
}
|
||||
@@ -247,6 +257,29 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
|
||||
{...form.getInputProps('url')}
|
||||
/>
|
||||
</Group>
|
||||
<TextInput
|
||||
disabled={isServerLock}
|
||||
label={t('form.addServer.input', {
|
||||
context: 'remoteUrl',
|
||||
postProcess: 'titleCase',
|
||||
})}
|
||||
placeholder={t('form.addServer.input', {
|
||||
context: 'remoteUrlPlaceholder',
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
{...form.getInputProps('remoteUrl')}
|
||||
/>
|
||||
{form.values.remoteUrl && (
|
||||
<Checkbox
|
||||
label={t('form.addServer.input', {
|
||||
context: 'preferRemoteUrl',
|
||||
postProcess: 'titleCase',
|
||||
})}
|
||||
{...form.getInputProps('preferRemoteUrl', {
|
||||
type: 'checkbox',
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
<TextInput
|
||||
label={t('form.addServer.input', {
|
||||
context: 'username',
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import i18n from '/@/i18n/i18n';
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryClient } from '/@/renderer/lib/react-query';
|
||||
import { useAuthStoreActions } from '/@/renderer/store';
|
||||
import { getServerById, useAuthStoreActions } from '/@/renderer/store';
|
||||
import { Checkbox } from '/@/shared/components/checkbox/checkbox';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
@@ -55,6 +55,8 @@ export const EditServerForm = ({ isUpdate, onCancel, password, server }: EditSer
|
||||
name: server?.name,
|
||||
password: password || '',
|
||||
preferInstantMix: server.preferInstantMix,
|
||||
preferRemoteUrl: server?.preferRemoteUrl || false,
|
||||
remoteUrl: server?.remoteUrl || '',
|
||||
savePassword: server.savePassword,
|
||||
type: server?.type,
|
||||
url: server?.url,
|
||||
@@ -66,43 +68,81 @@ export const EditServerForm = ({ isUpdate, onCancel, password, server }: EditSer
|
||||
const isNavidrome = form.values.type === ServerType.NAVIDROME;
|
||||
|
||||
const handleSubmit = form.onSubmit(async (values) => {
|
||||
const authFunction = api.controller.authenticate;
|
||||
|
||||
if (!authFunction) {
|
||||
return toast.error({
|
||||
message: t('error.invalidServer', { postProcess: 'sentenceCase' }),
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const data: AuthenticationResponse | undefined = await authFunction(
|
||||
values.url,
|
||||
{
|
||||
legacy: values.legacyAuth,
|
||||
password: values.password,
|
||||
username: values.username,
|
||||
},
|
||||
values.type,
|
||||
);
|
||||
|
||||
if (!data) {
|
||||
return toast.error({
|
||||
message: t('error.authenticationFailed', { postProcess: 'sentenceCase' }),
|
||||
});
|
||||
// Check if we can skip authentication
|
||||
const usernameChanged = values.username !== server.username;
|
||||
const passwordProvided = values.password && values.password.trim() !== '';
|
||||
const urlChanged = values.url !== server.url;
|
||||
const typeChanged = values.type !== server.type;
|
||||
|
||||
// Skip authentication if username hasn't changed, password is empty, and URL/type haven't changed
|
||||
const canSkipAuth =
|
||||
!usernameChanged && !passwordProvided && !urlChanged && !typeChanged;
|
||||
|
||||
let data: AuthenticationResponse | undefined;
|
||||
let serverItem: ServerListItemWithCredential;
|
||||
|
||||
if (canSkipAuth) {
|
||||
// Use existing server credentials
|
||||
const existingServer = getServerById(server.id);
|
||||
if (!existingServer) {
|
||||
return toast.error({
|
||||
message: t('error.invalidServer', { postProcess: 'sentenceCase' }),
|
||||
});
|
||||
}
|
||||
|
||||
serverItem = {
|
||||
...existingServer,
|
||||
id: server.id,
|
||||
name: values.name,
|
||||
type: values.type,
|
||||
url: values.url,
|
||||
};
|
||||
} else {
|
||||
// Need to authenticate
|
||||
const authFunction = api.controller.authenticate;
|
||||
|
||||
if (!authFunction) {
|
||||
return toast.error({
|
||||
message: t('error.invalidServer', { postProcess: 'sentenceCase' }),
|
||||
});
|
||||
}
|
||||
|
||||
data = await authFunction(
|
||||
values.url,
|
||||
{
|
||||
legacy: values.legacyAuth,
|
||||
password: values.password,
|
||||
username: values.username,
|
||||
},
|
||||
values.type,
|
||||
);
|
||||
|
||||
if (!data) {
|
||||
return toast.error({
|
||||
message: t('error.authenticationFailed', { postProcess: 'sentenceCase' }),
|
||||
});
|
||||
}
|
||||
|
||||
serverItem = {
|
||||
credential: data.credential,
|
||||
id: server.id,
|
||||
isAdmin: data.isAdmin,
|
||||
name: values.name,
|
||||
type: values.type,
|
||||
url: values.url,
|
||||
userId: data.userId,
|
||||
username: data.username,
|
||||
};
|
||||
|
||||
if (data.ndCredential !== undefined) {
|
||||
serverItem.ndCredential = data.ndCredential;
|
||||
}
|
||||
}
|
||||
|
||||
const serverItem: ServerListItemWithCredential = {
|
||||
credential: data.credential,
|
||||
id: server.id,
|
||||
isAdmin: data.isAdmin,
|
||||
name: values.name,
|
||||
type: values.type,
|
||||
url: values.url,
|
||||
userId: data.userId,
|
||||
username: data.username,
|
||||
};
|
||||
|
||||
// Update optional fields
|
||||
if (values.preferInstantMix !== undefined) {
|
||||
serverItem.preferInstantMix = values.preferInstantMix;
|
||||
}
|
||||
@@ -111,8 +151,14 @@ export const EditServerForm = ({ isUpdate, onCancel, password, server }: EditSer
|
||||
serverItem.savePassword = values.savePassword;
|
||||
}
|
||||
|
||||
if (data.ndCredential !== undefined) {
|
||||
serverItem.ndCredential = data.ndCredential;
|
||||
if (values.remoteUrl?.trim()) {
|
||||
serverItem.remoteUrl = values.remoteUrl.trim().replace(/\/$/, '');
|
||||
} else {
|
||||
serverItem.remoteUrl = undefined;
|
||||
}
|
||||
|
||||
if (values.preferRemoteUrl !== undefined) {
|
||||
serverItem.preferRemoteUrl = values.preferRemoteUrl;
|
||||
}
|
||||
|
||||
updateServer(server.id, serverItem);
|
||||
@@ -120,19 +166,29 @@ export const EditServerForm = ({ isUpdate, onCancel, password, server }: EditSer
|
||||
message: t('form.updateServer.title', { postProcess: 'sentenceCase' }),
|
||||
});
|
||||
|
||||
// Handle password saving in local settings
|
||||
if (localSettings) {
|
||||
if (values.savePassword) {
|
||||
const saved = await localSettings.passwordSet(values.password, server.id);
|
||||
if (!saved) {
|
||||
toast.error({
|
||||
message: t('form.addServer.error', {
|
||||
context: 'savePassword',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
});
|
||||
if (canSkipAuth) {
|
||||
// If we skipped auth, only update savePassword preference
|
||||
// Don't change the actual saved password
|
||||
if (!values.savePassword) {
|
||||
localSettings.passwordRemove(server.id);
|
||||
}
|
||||
} else {
|
||||
localSettings.passwordRemove(server.id);
|
||||
// If we authenticated, update password if savePassword is enabled
|
||||
if (values.savePassword && passwordProvided) {
|
||||
const saved = await localSettings.passwordSet(values.password, server.id);
|
||||
if (!saved) {
|
||||
toast.error({
|
||||
message: t('form.addServer.error', {
|
||||
context: 'savePassword',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
});
|
||||
}
|
||||
} else if (!values.savePassword) {
|
||||
localSettings.passwordRemove(server.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +223,32 @@ export const EditServerForm = ({ isUpdate, onCancel, password, server }: EditSer
|
||||
rightSection={form.isDirty('url') && <ModifiedFieldIndicator />}
|
||||
{...form.getInputProps('url')}
|
||||
/>
|
||||
<TextInput
|
||||
label={t('form.addServer.input', {
|
||||
context: 'remoteUrl',
|
||||
postProcess: 'titleCase',
|
||||
})}
|
||||
placeholder={t('form.addServer.input', {
|
||||
context: 'remoteUrlPlaceholder',
|
||||
postProcess: 'sentenceCase',
|
||||
})}
|
||||
rightSection={form.isDirty('remoteUrl') && <ModifiedFieldIndicator />}
|
||||
{...form.getInputProps('remoteUrl')}
|
||||
/>
|
||||
{form.values.remoteUrl && (
|
||||
<Group gap="xs">
|
||||
<Checkbox
|
||||
label={t('form.addServer.input', {
|
||||
context: 'preferRemoteUrl',
|
||||
postProcess: 'titleCase',
|
||||
})}
|
||||
{...form.getInputProps('preferRemoteUrl', {
|
||||
type: 'checkbox',
|
||||
})}
|
||||
/>
|
||||
{form.isDirty('preferRemoteUrl') && <ModifiedFieldIndicator />}
|
||||
</Group>
|
||||
)}
|
||||
<TextInput
|
||||
label={t('form.addServer.input', {
|
||||
context: 'username',
|
||||
@@ -182,7 +264,6 @@ export const EditServerForm = ({ isUpdate, onCancel, password, server }: EditSer
|
||||
context: 'password',
|
||||
postProcess: 'titleCase',
|
||||
})}
|
||||
required={isNavidrome || isSubsonic}
|
||||
{...form.getInputProps('password')}
|
||||
/>
|
||||
{localSettings && isNavidrome && (
|
||||
|
||||
Reference in New Issue
Block a user