mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:27:09 -05:00 
			
		
		
		
	feat(cli): Use well-known endpoint to resolve API (#6733)
* feat(cli): use immich-well-known * chore: e2e test --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
		
							parent
							
								
									1c93ef1916
								
							
						
					
					
						commit
						4dc0fc45e7
					
				@ -3,6 +3,7 @@ import { access, constants, mkdir, readFile, unlink, writeFile } from 'node:fs/p
 | 
				
			|||||||
import path from 'node:path';
 | 
					import path from 'node:path';
 | 
				
			||||||
import yaml from 'yaml';
 | 
					import yaml from 'yaml';
 | 
				
			||||||
import { ImmichApi } from './api.service';
 | 
					import { ImmichApi } from './api.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LoginError extends Error {
 | 
					class LoginError extends Error {
 | 
				
			||||||
  constructor(message: string) {
 | 
					  constructor(message: string) {
 | 
				
			||||||
    super(message);
 | 
					    super(message);
 | 
				
			||||||
@ -14,14 +15,12 @@ class LoginError extends Error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class SessionService {
 | 
					export class SessionService {
 | 
				
			||||||
  readonly configDirectory!: string;
 | 
					  private get authPath() {
 | 
				
			||||||
  readonly authPath!: string;
 | 
					    return path.join(this.configDirectory, '/auth.yml');
 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor(configDirectory: string) {
 | 
					 | 
				
			||||||
    this.configDirectory = configDirectory;
 | 
					 | 
				
			||||||
    this.authPath = path.join(configDirectory, '/auth.yml');
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(private configDirectory: string) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async connect(): Promise<ImmichApi> {
 | 
					  async connect(): Promise<ImmichApi> {
 | 
				
			||||||
    let instanceUrl = process.env.IMMICH_INSTANCE_URL;
 | 
					    let instanceUrl = process.env.IMMICH_INSTANCE_URL;
 | 
				
			||||||
    let apiKey = process.env.IMMICH_API_KEY;
 | 
					    let apiKey = process.env.IMMICH_API_KEY;
 | 
				
			||||||
@ -48,6 +47,8 @@ export class SessionService {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    instanceUrl = await this.resolveApiEndpoint(instanceUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const api = new ImmichApi(instanceUrl, apiKey);
 | 
					    const api = new ImmichApi(instanceUrl, apiKey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const pingResponse = await api.pingServer().catch((error) => {
 | 
					    const pingResponse = await api.pingServer().catch((error) => {
 | 
				
			||||||
@ -62,7 +63,9 @@ export class SessionService {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async login(instanceUrl: string, apiKey: string): Promise<ImmichApi> {
 | 
					  async login(instanceUrl: string, apiKey: string): Promise<ImmichApi> {
 | 
				
			||||||
    console.log('Logging in...');
 | 
					    console.log(`Logging in to ${instanceUrl}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    instanceUrl = await this.resolveApiEndpoint(instanceUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const api = new ImmichApi(instanceUrl, apiKey);
 | 
					    const api = new ImmichApi(instanceUrl, apiKey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -83,7 +86,7 @@ export class SessionService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    await writeFile(this.authPath, yaml.stringify({ instanceUrl, apiKey }), { mode: 0o600 });
 | 
					    await writeFile(this.authPath, yaml.stringify({ instanceUrl, apiKey }), { mode: 0o600 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    console.log('Wrote auth info to ' + this.authPath);
 | 
					    console.log(`Wrote auth info to ${this.authPath}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return api;
 | 
					    return api;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -98,4 +101,18 @@ export class SessionService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    console.log('Successfully logged out');
 | 
					    console.log('Successfully logged out');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private async resolveApiEndpoint(instanceUrl: string): Promise<string> {
 | 
				
			||||||
 | 
					    const wellKnownUrl = new URL('.well-known/immich', instanceUrl);
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const wellKnown = await fetch(wellKnownUrl).then((response) => response.json());
 | 
				
			||||||
 | 
					      const endpoint = new URL(wellKnown.api.endpoint, instanceUrl).toString();
 | 
				
			||||||
 | 
					      if (endpoint !== instanceUrl) {
 | 
				
			||||||
 | 
					        console.debug(`Discovered API at ${endpoint}`);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return endpoint;
 | 
				
			||||||
 | 
					    } catch {
 | 
				
			||||||
 | 
					      return instanceUrl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -29,12 +29,12 @@ describe(`immich login-key`, () => {
 | 
				
			|||||||
    expect(exitCode).toBe(1);
 | 
					    expect(exitCode).toBe(1);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should login', async () => {
 | 
					  it('should login and save auth.yml with 600', async () => {
 | 
				
			||||||
    const admin = await apiUtils.adminSetup();
 | 
					    const admin = await apiUtils.adminSetup();
 | 
				
			||||||
    const key = await apiUtils.createApiKey(admin.accessToken);
 | 
					    const key = await apiUtils.createApiKey(admin.accessToken);
 | 
				
			||||||
    const { stdout, stderr, exitCode } = await immichCli(['login-key', app, `${key.secret}`]);
 | 
					    const { stdout, stderr, exitCode } = await immichCli(['login-key', app, `${key.secret}`]);
 | 
				
			||||||
    expect(stdout.split('\n')).toEqual([
 | 
					    expect(stdout.split('\n')).toEqual([
 | 
				
			||||||
      'Logging in...',
 | 
					      'Logging in to http://127.0.0.1:2283/api',
 | 
				
			||||||
      'Logged in as admin@immich.cloud',
 | 
					      'Logged in as admin@immich.cloud',
 | 
				
			||||||
      'Wrote auth info to /tmp/immich/auth.yml',
 | 
					      'Wrote auth info to /tmp/immich/auth.yml',
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
@ -45,4 +45,18 @@ describe(`immich login-key`, () => {
 | 
				
			|||||||
    const mode = (stats.mode & 0o777).toString(8);
 | 
					    const mode = (stats.mode & 0o777).toString(8);
 | 
				
			||||||
    expect(mode).toEqual('600');
 | 
					    expect(mode).toEqual('600');
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should login without /api in the url', async () => {
 | 
				
			||||||
 | 
					    const admin = await apiUtils.adminSetup();
 | 
				
			||||||
 | 
					    const key = await apiUtils.createApiKey(admin.accessToken);
 | 
				
			||||||
 | 
					    const { stdout, stderr, exitCode } = await immichCli(['login-key', app.replaceAll('/api', ''), `${key.secret}`]);
 | 
				
			||||||
 | 
					    expect(stdout.split('\n')).toEqual([
 | 
				
			||||||
 | 
					      'Logging in to http://127.0.0.1:2283',
 | 
				
			||||||
 | 
					      'Discovered API at http://127.0.0.1:2283/api',
 | 
				
			||||||
 | 
					      'Logged in as admin@immich.cloud',
 | 
				
			||||||
 | 
					      'Wrote auth info to /tmp/immich/auth.yml',
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					    expect(stderr).toBe('');
 | 
				
			||||||
 | 
					    expect(exitCode).toBe(0);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user