mirror of
https://github.com/immich-app/immich.git
synced 2025-06-01 04:36:19 -04:00
chore(cli): rename commands (#8200)
* chore(cli): rename login command * chore: rename key/url
This commit is contained in:
parent
5b7417bf64
commit
75aa8e6621
@ -3,12 +3,12 @@ import { existsSync } from 'node:fs';
|
|||||||
import { mkdir, unlink } from 'node:fs/promises';
|
import { mkdir, unlink } from 'node:fs/promises';
|
||||||
import { BaseOptions, connect, getAuthFilePath, logError, withError, writeAuthFile } from 'src/utils';
|
import { BaseOptions, connect, getAuthFilePath, logError, withError, writeAuthFile } from 'src/utils';
|
||||||
|
|
||||||
export const login = async (instanceUrl: string, apiKey: string, options: BaseOptions) => {
|
export const login = async (url: string, key: string, options: BaseOptions) => {
|
||||||
console.log(`Logging in to ${instanceUrl}`);
|
console.log(`Logging in to ${url}`);
|
||||||
|
|
||||||
const { configDirectory: configDir } = options;
|
const { configDirectory: configDir } = options;
|
||||||
|
|
||||||
await connect(instanceUrl, apiKey);
|
await connect(url, key);
|
||||||
|
|
||||||
const [error, userInfo] = await withError(getMyUserInfo());
|
const [error, userInfo] = await withError(getMyUserInfo());
|
||||||
if (error) {
|
if (error) {
|
||||||
@ -27,7 +27,7 @@ export const login = async (instanceUrl: string, apiKey: string, options: BaseOp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await writeAuthFile(configDir, { instanceUrl, apiKey });
|
await writeAuthFile(configDir, { url, key });
|
||||||
|
|
||||||
console.log(`Wrote auth info to ${getAuthFilePath(configDir)}`);
|
console.log(`Wrote auth info to ${getAuthFilePath(configDir)}`);
|
||||||
};
|
};
|
||||||
|
@ -19,7 +19,7 @@ const program = new Command()
|
|||||||
.default(defaultConfigDirectory),
|
.default(defaultConfigDirectory),
|
||||||
)
|
)
|
||||||
.addOption(new Option('-u, --url [url]', 'Immich server URL').env('IMMICH_INSTANCE_URL'))
|
.addOption(new Option('-u, --url [url]', 'Immich server URL').env('IMMICH_INSTANCE_URL'))
|
||||||
.addOption(new Option('-k, --key [apiKey]', 'Immich API key').env('IMMICH_API_KEY'));
|
.addOption(new Option('-k, --key [key]', 'Immich API key').env('IMMICH_API_KEY'));
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('login')
|
.command('login')
|
||||||
|
@ -8,44 +8,42 @@ import yaml from 'yaml';
|
|||||||
|
|
||||||
export interface BaseOptions {
|
export interface BaseOptions {
|
||||||
configDirectory: string;
|
configDirectory: string;
|
||||||
apiKey?: string;
|
key?: string;
|
||||||
instanceUrl?: string;
|
url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthDto {
|
export type AuthDto = { url: string; key: string };
|
||||||
instanceUrl: string;
|
type OldAuthDto = { instanceUrl: string; apiKey: string };
|
||||||
apiKey: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const authenticate = async (options: BaseOptions): Promise<void> => {
|
export const authenticate = async (options: BaseOptions): Promise<void> => {
|
||||||
const { configDirectory: configDir, instanceUrl, apiKey } = options;
|
const { configDirectory: configDir, url, key } = options;
|
||||||
|
|
||||||
// provided in command
|
// provided in command
|
||||||
if (instanceUrl && apiKey) {
|
if (url && key) {
|
||||||
await connect(instanceUrl, apiKey);
|
await connect(url, key);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallback to file
|
// fallback to auth file
|
||||||
const config = await readAuthFile(configDir);
|
const config = await readAuthFile(configDir);
|
||||||
await connect(config.instanceUrl, config.apiKey);
|
await connect(config.url, config.key);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const connect = async (instanceUrl: string, apiKey: string): Promise<void> => {
|
export const connect = async (url: string, key: string): Promise<void> => {
|
||||||
const wellKnownUrl = new URL('.well-known/immich', instanceUrl);
|
const wellKnownUrl = new URL('.well-known/immich', url);
|
||||||
try {
|
try {
|
||||||
const wellKnown = await fetch(wellKnownUrl).then((response) => response.json());
|
const wellKnown = await fetch(wellKnownUrl).then((response) => response.json());
|
||||||
const endpoint = new URL(wellKnown.api.endpoint, instanceUrl).toString();
|
const endpoint = new URL(wellKnown.api.endpoint, url).toString();
|
||||||
if (endpoint !== instanceUrl) {
|
if (endpoint !== url) {
|
||||||
console.debug(`Discovered API at ${endpoint}`);
|
console.debug(`Discovered API at ${endpoint}`);
|
||||||
}
|
}
|
||||||
instanceUrl = endpoint;
|
url = endpoint;
|
||||||
} catch {
|
} catch {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
defaults.baseUrl = instanceUrl;
|
defaults.baseUrl = url;
|
||||||
defaults.headers = { 'x-api-key': apiKey };
|
defaults.headers = { 'x-api-key': key };
|
||||||
|
|
||||||
const [error] = await withError(getMyUserInfo());
|
const [error] = await withError(getMyUserInfo());
|
||||||
if (isHttpError(error)) {
|
if (isHttpError(error)) {
|
||||||
@ -69,7 +67,12 @@ export const readAuthFile = async (dir: string) => {
|
|||||||
try {
|
try {
|
||||||
const data = await readFile(getAuthFilePath(dir));
|
const data = await readFile(getAuthFilePath(dir));
|
||||||
// TODO add class-transform/validation
|
// TODO add class-transform/validation
|
||||||
return yaml.parse(data.toString()) as AuthDto;
|
const auth = yaml.parse(data.toString()) as AuthDto | OldAuthDto;
|
||||||
|
const { instanceUrl, apiKey } = auth as OldAuthDto;
|
||||||
|
if (instanceUrl && apiKey) {
|
||||||
|
return { url: instanceUrl, key: apiKey };
|
||||||
|
}
|
||||||
|
return auth as AuthDto;
|
||||||
} catch (error: Error | any) {
|
} catch (error: Error | any) {
|
||||||
if (error.code === 'ENOENT' || error.code === 'ENOTDIR') {
|
if (error.code === 'ENOENT' || error.code === 'ENOTDIR') {
|
||||||
console.log('No auth file exists. Please login first.');
|
console.log('No auth file exists. Please login first.');
|
||||||
|
@ -61,7 +61,7 @@ Options:
|
|||||||
Commands:
|
Commands:
|
||||||
upload [options] [paths...] Upload assets
|
upload [options] [paths...] Upload assets
|
||||||
server-info Display server information
|
server-info Display server information
|
||||||
login-key [instanceUrl] [apiKey] Login using an API key
|
login [url] [key] Login using an API key
|
||||||
logout Remove stored credentials
|
logout Remove stored credentials
|
||||||
help [command] display help for command
|
help [command] display help for command
|
||||||
```
|
```
|
||||||
@ -97,13 +97,13 @@ Note that the above options can read from environment variables as well.
|
|||||||
You begin by authenticating to your Immich server.
|
You begin by authenticating to your Immich server.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
immich login-key [instanceUrl] [apiKey]
|
immich login [url] [key]
|
||||||
```
|
```
|
||||||
|
|
||||||
For instance,
|
For instance,
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
immich login-key http://192.168.1.216:2283/api HFEJ38DNSDUEG
|
immich login http://192.168.1.216:2283/api HFEJ38DNSDUEG
|
||||||
```
|
```
|
||||||
|
|
||||||
This will store your credentials in a `auth.yml` file in the configuration directory which defaults to `~/.config/`. The directory can be set with the `-d` option or the environment variable `IMMICH_CONFIG_DIR`. Please keep the file secure, either by performing the logout command after you are done, or deleting it manually.
|
This will store your credentials in a `auth.yml` file in the configuration directory which defaults to `~/.config/`. The directory can be set with the `-d` option or the environment variable `IMMICH_CONFIG_DIR`. Please keep the file secure, either by performing the logout command after you are done, or deleting it manually.
|
||||||
|
@ -2,25 +2,25 @@ import { stat } from 'node:fs/promises';
|
|||||||
import { app, immichCli, utils } from 'src/utils';
|
import { app, immichCli, utils } from 'src/utils';
|
||||||
import { beforeEach, describe, expect, it } from 'vitest';
|
import { beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
describe(`immich login-key`, () => {
|
describe(`immich login`, () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await utils.resetDatabase();
|
await utils.resetDatabase();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require a url', async () => {
|
it('should require a url', async () => {
|
||||||
const { stderr, exitCode } = await immichCli(['login-key']);
|
const { stderr, exitCode } = await immichCli(['login']);
|
||||||
expect(stderr).toBe("error: missing required argument 'url'");
|
expect(stderr).toBe("error: missing required argument 'url'");
|
||||||
expect(exitCode).toBe(1);
|
expect(exitCode).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require a key', async () => {
|
it('should require a key', async () => {
|
||||||
const { stderr, exitCode } = await immichCli(['login-key', app]);
|
const { stderr, exitCode } = await immichCli(['login', app]);
|
||||||
expect(stderr).toBe("error: missing required argument 'key'");
|
expect(stderr).toBe("error: missing required argument 'key'");
|
||||||
expect(exitCode).toBe(1);
|
expect(exitCode).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require a valid key', async () => {
|
it('should require a valid key', async () => {
|
||||||
const { stderr, exitCode } = await immichCli(['login-key', app, 'immich-is-so-cool']);
|
const { stderr, exitCode } = await immichCli(['login', app, 'immich-is-so-cool']);
|
||||||
expect(stderr).toContain('Failed to connect to server');
|
expect(stderr).toContain('Failed to connect to server');
|
||||||
expect(stderr).toContain('Invalid API key');
|
expect(stderr).toContain('Invalid API key');
|
||||||
expect(stderr).toContain('401');
|
expect(stderr).toContain('401');
|
||||||
@ -30,7 +30,7 @@ describe(`immich login-key`, () => {
|
|||||||
it('should login and save auth.yml with 600', async () => {
|
it('should login and save auth.yml with 600', async () => {
|
||||||
const admin = await utils.adminSetup();
|
const admin = await utils.adminSetup();
|
||||||
const key = await utils.createApiKey(admin.accessToken);
|
const key = await utils.createApiKey(admin.accessToken);
|
||||||
const { stdout, stderr, exitCode } = await immichCli(['login-key', app, `${key.secret}`]);
|
const { stdout, stderr, exitCode } = await immichCli(['login', app, `${key.secret}`]);
|
||||||
expect(stdout.split('\n')).toEqual([
|
expect(stdout.split('\n')).toEqual([
|
||||||
'Logging in to http://127.0.0.1:2283/api',
|
'Logging in to http://127.0.0.1:2283/api',
|
||||||
'Logged in as admin@immich.cloud',
|
'Logged in as admin@immich.cloud',
|
||||||
@ -47,7 +47,7 @@ describe(`immich login-key`, () => {
|
|||||||
it('should login without /api in the url', async () => {
|
it('should login without /api in the url', async () => {
|
||||||
const admin = await utils.adminSetup();
|
const admin = await utils.adminSetup();
|
||||||
const key = await utils.createApiKey(admin.accessToken);
|
const key = await utils.createApiKey(admin.accessToken);
|
||||||
const { stdout, stderr, exitCode } = await immichCli(['login-key', app.replaceAll('/api', ''), `${key.secret}`]);
|
const { stdout, stderr, exitCode } = await immichCli(['login', app.replaceAll('/api', ''), `${key.secret}`]);
|
||||||
expect(stdout.split('\n')).toEqual([
|
expect(stdout.split('\n')).toEqual([
|
||||||
'Logging in to http://127.0.0.1:2283',
|
'Logging in to http://127.0.0.1:2283',
|
||||||
'Discovered API at http://127.0.0.1:2283/api',
|
'Discovered API at http://127.0.0.1:2283/api',
|
||||||
|
@ -406,7 +406,7 @@ export const utils = {
|
|||||||
|
|
||||||
cliLogin: async (accessToken: string) => {
|
cliLogin: async (accessToken: string) => {
|
||||||
const key = await utils.createApiKey(accessToken);
|
const key = await utils.createApiKey(accessToken);
|
||||||
await immichCli(['login-key', app, `${key.secret}`]);
|
await immichCli(['login', app, `${key.secret}`]);
|
||||||
return key.secret;
|
return key.secret;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user