feat: dynamic authentication url and authentication using website token

This commit is contained in:
Michal Szczepanski 2023-10-11 07:43:53 +02:00
parent bc5414a27c
commit 06de9cbac9
35 changed files with 264 additions and 116 deletions

View File

@ -1,4 +1,4 @@
VERSION=1 VERSION=1
WEB_URL=http://127.0.0.1:5173 WEB_URL=http://localhost:5173
IS_PRODUCTION=false IS_PRODUCTION=false
OBJ_LIST_LIMIT=20 OBJ_LIST_LIMIT=20

View File

@ -65,10 +65,10 @@ export const environmentConfig: EnvironmentConfig = {
skipCssImageSizeMB: 2, skipCssImageSizeMB: 2,
expertMode: false, expertMode: false,
history: { history: {
pinComment: true, pinComment: false,
pinDraw: true, pinDraw: false,
pageComment: true, pageComment: false,
pageNote: true pageNote: false
} }
}, },
objListLimit: parseInt(process.env.OBJ_LIST_LIMIT || '100000') objListLimit: parseInt(process.env.OBJ_LIST_LIMIT || '100000')

View File

@ -50,6 +50,7 @@ export enum BusMessageType {
POPUP_IS_PDF = 'popup.is.pdf', POPUP_IS_PDF = 'popup.is.pdf',
// Content script // Content script
CONTENT_DOWNLOAD_DATA = 'content.download', CONTENT_DOWNLOAD_DATA = 'content.download',
CONTENT_EXTENSION_LOGIN = 'content.extension.login',
CONTENT_INVALIDATE = 'content.invalidate', CONTENT_INVALIDATE = 'content.invalidate',
CONTENT_PING_URL = 'content.ping.url', CONTENT_PING_URL = 'content.ping.url',
CONTENT_PIN_VISIBLE = 'content.pin.visible', CONTENT_PIN_VISIBLE = 'content.pin.visible',

View File

@ -16,4 +16,5 @@
*/ */
export interface ObjServerDto { export interface ObjServerDto {
id: number; id: number;
sub: string;
} }

View File

@ -0,0 +1,30 @@
/*
* This file is part of the pinmenote-extension distribution (https://github.com/pinmenote/pinmenote-extension).
* Copyright (c) 2023 Michal Szczepanski.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { BrowserApi } from '@pinmenote/browser-api';
import { BusMessageType } from '../../../common/model/bus.model';
import { ICommand } from '../../../common/model/shared/common.dto';
import { TokenStorageGetCommand } from '../../../common/command/server/token/token-storage-get.command';
export class LoginExtensionCommand implements ICommand<Promise<void>> {
async execute(): Promise<void> {
const token = localStorage.getItem('accessToken');
const extensionToken = await new TokenStorageGetCommand().execute();
// we are logged in on website but not on extension
if (!extensionToken && token)
await BrowserApi.sendRuntimeMessage({ type: BusMessageType.CONTENT_EXTENSION_LOGIN, data: JSON.parse(token) });
}
}

View File

@ -34,6 +34,8 @@ import { RuntimePinGetHrefCommand } from './command/runtime/runtime-pin-get-href
import { TinyDispatcher } from '@pinmenote/tiny-dispatcher'; import { TinyDispatcher } from '@pinmenote/tiny-dispatcher';
import { UrlFactory } from '../common/factory/url.factory'; import { UrlFactory } from '../common/factory/url.factory';
import { fnUid } from '../common/fn/fn-uid'; import { fnUid } from '../common/fn/fn-uid';
import { environmentConfig } from '../common/environment';
import { LoginExtensionCommand } from './command/login/login-extension.command';
class PinMeScript { class PinMeScript {
private href: string; private href: string;
@ -56,6 +58,8 @@ class PinMeScript {
private handlePinSettings = async (event: string, key: string): Promise<void> => { private handlePinSettings = async (event: string, key: string): Promise<void> => {
TinyDispatcher.getInstance().removeListener(event, key); TinyDispatcher.getInstance().removeListener(event, key);
if (location.origin === environmentConfig.defaultServer) await new LoginExtensionCommand().execute();
await ContentSettingsStore.initSettings(); await ContentSettingsStore.initSettings();
await new RuntimePinGetHrefCommand().execute(); await new RuntimePinGetHrefCommand().execute();

View File

@ -0,0 +1,27 @@
/*
* This file is part of the pinmenote-extension distribution (https://github.com/pinmenote/pinmenote-extension).
* Copyright (c) 2023 Michal Szczepanski.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { FetchService } from '@pinmenote/fetch-service';
import { ICommand } from '../../../common/model/shared/common.dto';
import { environmentConfig } from '../../../common/environment';
export class ApiAuthUrlCommand implements ICommand<Promise<string>> {
async execute(): Promise<string> {
const req = await FetchService.fetch<{ auth: string }>(`${environmentConfig.defaultServer}/api/server.json`, {});
if (!req.ok) throw new Error('Invalid api url');
return req.data.auth;
}
}

View File

@ -18,7 +18,6 @@ import { AccessTokenDto, TokenDataDto } from '../../../common/model/shared/token
import { FetchHeaders, RefreshTokenParams } from '@pinmenote/fetch-service'; import { FetchHeaders, RefreshTokenParams } from '@pinmenote/fetch-service';
import { TokenStorageGetCommand } from '../../../common/command/server/token/token-storage-get.command'; import { TokenStorageGetCommand } from '../../../common/command/server/token/token-storage-get.command';
import { TokenStorageSetCommand } from '../../../common/command/server/token/token-storage-set.command'; import { TokenStorageSetCommand } from '../../../common/command/server/token/token-storage-set.command';
import { environmentConfig } from '../../../common/environment';
import { fnConsoleLog } from '../../../common/fn/fn-console'; import { fnConsoleLog } from '../../../common/fn/fn-console';
import jwtDecode from 'jwt-decode'; import jwtDecode from 'jwt-decode';
@ -26,10 +25,6 @@ export class ApiCallBase {
protected token: AccessTokenDto | undefined; protected token: AccessTokenDto | undefined;
protected tokenData: TokenDataDto | undefined; protected tokenData: TokenDataDto | undefined;
get apiUrl(): string {
return environmentConfig.defaultServer;
}
get storeUrl(): string | undefined { get storeUrl(): string | undefined {
return this.tokenData?.data.store; return this.tokenData?.data.store;
} }
@ -40,31 +35,29 @@ export class ApiCallBase {
this.tokenData = jwtDecode<TokenDataDto>(this.token.access_token); this.tokenData = jwtDecode<TokenDataDto>(this.token.access_token);
} }
protected refreshParams(): RefreshTokenParams { protected refreshParams(baseUrl: string): RefreshTokenParams {
return { return {
data: { data: {
refreshKey: 'message', refreshKey: 'message',
refreshValue: 'jwt expired', refreshValue: 'jwt expired',
method: 'PUT', method: 'PUT',
url: `${this.apiUrl}/api/v1/auth/refresh-token` url: `${baseUrl}/api/v1/refresh-token`
}, },
successCallback: (res) => { successCallback: (res, headers) => {
if (res.status === 200) {
const value: AccessTokenDto = JSON.parse(res.data);
new TokenStorageSetCommand(value)
.execute()
.then(() => {
/* IGNORE */
})
.catch(() => {
/* IGNORE */
});
return {
Authorization: `Bearer ${value.access_token}`
} as FetchHeaders;
}
fnConsoleLog('refreshParams->successCallback', res); fnConsoleLog('refreshParams->successCallback', res);
return {}; const value: AccessTokenDto = JSON.parse(res.data);
new TokenStorageSetCommand(value)
.execute()
.then(() => {
/* IGNORE */
})
.catch(() => {
/* IGNORE */
});
return {
...headers,
Authorization: `Bearer ${value.access_token}`
} as FetchHeaders;
}, },
errorCallback: (error) => { errorCallback: (error) => {
fnConsoleLog('refreshParams->errorCallback', error); fnConsoleLog('refreshParams->errorCallback', error);

View File

@ -20,6 +20,7 @@ import { ICommand, ServerErrorDto } from '../../../common/model/shared/common.dt
import { ApiCallBase } from './api-call.base'; import { ApiCallBase } from './api-call.base';
import { apiResponseError } from './api.model'; import { apiResponseError } from './api.model';
import { fnConsoleLog } from '../../../common/fn/fn-console'; import { fnConsoleLog } from '../../../common/fn/fn-console';
import { ApiAuthUrlCommand } from './api-auth-url.command';
export class ApiLoginCommand export class ApiLoginCommand
extends ApiCallBase extends ApiCallBase
@ -31,7 +32,8 @@ export class ApiLoginCommand
async execute(): Promise<FetchResponse<AccessTokenDto | ServerErrorDto>> { async execute(): Promise<FetchResponse<AccessTokenDto | ServerErrorDto>> {
fnConsoleLog('ApiLoginCommand->execute'); fnConsoleLog('ApiLoginCommand->execute');
const url = `${this.apiUrl}/api/v1/auth/login`; const baseUrl = await new ApiAuthUrlCommand().execute();
const url = `${baseUrl}/api/v1/login`;
try { try {
return await FetchService.fetch<AccessTokenDto | ServerErrorDto>(url, { return await FetchService.fetch<AccessTokenDto | ServerErrorDto>(url, {
method: 'POST', method: 'POST',

View File

@ -19,6 +19,7 @@ import { FetchResponse, FetchService } from '@pinmenote/fetch-service';
import { ApiCallBase } from './api-call.base'; import { ApiCallBase } from './api-call.base';
import { fnConsoleLog } from '../../../common/fn/fn-console'; import { fnConsoleLog } from '../../../common/fn/fn-console';
import { ApiErrorCode } from '../../../common/model/shared/api.error-code'; import { ApiErrorCode } from '../../../common/model/shared/api.error-code';
import { ApiAuthUrlCommand } from './api-auth-url.command';
export class ApiLogoutCommand export class ApiLogoutCommand
extends ApiCallBase extends ApiCallBase
@ -26,7 +27,8 @@ export class ApiLogoutCommand
{ {
async execute(): Promise<FetchResponse<BoolDto | ServerErrorDto>> { async execute(): Promise<FetchResponse<BoolDto | ServerErrorDto>> {
await this.initTokenData(); await this.initTokenData();
const url = `${this.apiUrl}/api/v1/auth/logout`; const baseUrl = await new ApiAuthUrlCommand().execute();
const url = `${baseUrl}/api/v1/logout`;
fnConsoleLog('ApiLogoutCommand->execute', url); fnConsoleLog('ApiLogoutCommand->execute', url);
try { try {
return await FetchService.fetch<BoolDto>( return await FetchService.fetch<BoolDto>(
@ -35,7 +37,7 @@ export class ApiLogoutCommand
method: 'POST', method: 'POST',
headers: this.getAuthHeaders() headers: this.getAuthHeaders()
}, },
this.refreshParams() this.refreshParams(baseUrl)
); );
} catch (e) { } catch (e) {
fnConsoleLog('ERROR', e); fnConsoleLog('ERROR', e);

View File

@ -20,6 +20,7 @@ import { ICommand, ServerErrorDto } from '../../../common/model/shared/common.dt
import { ApiCallBase } from './api-call.base'; import { ApiCallBase } from './api-call.base';
import { fnConsoleLog } from '../../../common/fn/fn-console'; import { fnConsoleLog } from '../../../common/fn/fn-console';
import { ApiErrorCode } from '../../../common/model/shared/api.error-code'; import { ApiErrorCode } from '../../../common/model/shared/api.error-code';
import { ApiAuthUrlCommand } from './api-auth-url.command';
export class ApiVerify2faCommand export class ApiVerify2faCommand
extends ApiCallBase extends ApiCallBase
@ -31,7 +32,8 @@ export class ApiVerify2faCommand
async execute(): Promise<FetchResponse<AccessTokenDto | ServerErrorDto>> { async execute(): Promise<FetchResponse<AccessTokenDto | ServerErrorDto>> {
fnConsoleLog('ApiVerify2faCommand->execute'); fnConsoleLog('ApiVerify2faCommand->execute');
const url = `${this.apiUrl}/api/v1/auth/2fa/verify`; const baseUrl = await new ApiAuthUrlCommand().execute();
const url = `${baseUrl}/api/v1/2fa/verify`;
try { try {
return await FetchService.fetch<AccessTokenDto>(url, { return await FetchService.fetch<AccessTokenDto>(url, {
method: 'POST', method: 'POST',

View File

@ -21,6 +21,9 @@ import { ICommand } from '../../../../common/model/shared/common.dto';
import { fnConsoleLog } from '../../../../common/fn/fn-console'; import { fnConsoleLog } from '../../../../common/fn/fn-console';
export class ApiStoreBeginCommand extends ApiCallBase implements ICommand<Promise<BeginTxResponse | undefined>> { export class ApiStoreBeginCommand extends ApiCallBase implements ICommand<Promise<BeginTxResponse | undefined>> {
constructor(private authUrl: string) {
super();
}
async execute(): Promise<BeginTxResponse | undefined> { async execute(): Promise<BeginTxResponse | undefined> {
await this.initTokenData(); await this.initTokenData();
if (!this.storeUrl) return; if (!this.storeUrl) return;
@ -28,7 +31,7 @@ export class ApiStoreBeginCommand extends ApiCallBase implements ICommand<Promis
const resp = await FetchService.fetch<BeginTxResponse>( const resp = await FetchService.fetch<BeginTxResponse>(
`${this.storeUrl}/api/v1/tx/begin`, `${this.storeUrl}/api/v1/tx/begin`,
{ headers: this.getAuthHeaders() }, { headers: this.getAuthHeaders() },
this.refreshParams() this.refreshParams(this.authUrl)
); );
return resp.data; return resp.data;
} catch (e) { } catch (e) {

View File

@ -21,7 +21,7 @@ import { ICommand } from '../../../../common/model/shared/common.dto';
import { fnConsoleLog } from '../../../../common/fn/fn-console'; import { fnConsoleLog } from '../../../../common/fn/fn-console';
export class ApiStoreCommitCommand extends ApiCallBase implements ICommand<Promise<boolean>> { export class ApiStoreCommitCommand extends ApiCallBase implements ICommand<Promise<boolean>> {
constructor(private tx: BeginTxResponse) { constructor(private authUrl: string, private tx: BeginTxResponse) {
super(); super();
} }
async execute(): Promise<boolean> { async execute(): Promise<boolean> {
@ -34,7 +34,7 @@ export class ApiStoreCommitCommand extends ApiCallBase implements ICommand<Promi
type: 'TEXT', type: 'TEXT',
headers: this.getAuthHeaders() headers: this.getAuthHeaders()
}, },
this.refreshParams() this.refreshParams(this.authUrl)
); );
return resp.ok; return resp.ok;
} catch (e) { } catch (e) {

View File

@ -29,10 +29,11 @@ export interface ObjAddRequest {
export interface ObjAddResponse { export interface ObjAddResponse {
serverId: number; serverId: number;
sub: string;
} }
export class ApiObjAddCommand extends ApiCallBase implements ICommand<Promise<ObjAddResponse | ServerErrorDto>> { export class ApiObjAddCommand extends ApiCallBase implements ICommand<Promise<ObjAddResponse | ServerErrorDto>> {
constructor(private obj: ObjDto, private hash: string, private tx: BeginTxResponse) { constructor(private authUrl: string, private obj: ObjDto, private hash: string, private tx: BeginTxResponse) {
super(); super();
} }
async execute(): Promise<ObjAddResponse | ServerErrorDto> { async execute(): Promise<ObjAddResponse | ServerErrorDto> {
@ -49,7 +50,7 @@ export class ApiObjAddCommand extends ApiCallBase implements ICommand<Promise<Ob
hash: this.hash hash: this.hash
}) })
}, },
this.refreshParams() this.refreshParams(this.authUrl)
); );
// fnConsoleLog('ApiStoreSyncInfoCommand->response', resp); // fnConsoleLog('ApiStoreSyncInfoCommand->response', resp);
return resp.data; return resp.data;

View File

@ -17,26 +17,34 @@
import { ApiCallBase } from '../../api-call.base'; import { ApiCallBase } from '../../api-call.base';
import { ICommand, ServerErrorDto } from '../../../../../common/model/shared/common.dto'; import { ICommand, ServerErrorDto } from '../../../../../common/model/shared/common.dto';
import { ApiErrorCode } from '../../../../../common/model/shared/api.error-code';
import { FetchService } from '@pinmenote/fetch-service'; import { FetchService } from '@pinmenote/fetch-service';
import { BeginTxResponse, ObjSingleChange } from '../api-store.model'; import { ObjSingleChange } from '../api-store.model';
import { fnConsoleLog } from '../../../../../common/fn/fn-console';
export class ApiObjGetByHashCommand extends ApiCallBase implements ICommand<Promise<ObjSingleChange | ServerErrorDto>> { export interface ObjSingleChangeSub extends ObjSingleChange {
constructor(private hash: string, private tx: BeginTxResponse) { sub: string;
}
export class ApiObjGetByHashCommand extends ApiCallBase implements ICommand<Promise<ObjSingleChangeSub>> {
constructor(private authUrl: string, private hash: string) {
super(); super();
} }
async execute(): Promise<ObjSingleChange | ServerErrorDto> { async execute(): Promise<ObjSingleChangeSub> {
await this.initTokenData(); await this.initTokenData();
if (!this.storeUrl) return { code: ApiErrorCode.INTERNAL, message: 'ApiStoreObjGetByHashCommand' }; if (!this.storeUrl) {
fnConsoleLog('ApiStoreObjGetByHashCommand', this.storeUrl);
throw new Error('PROBLEM !!!!!!!!!!!!!!!');
}
const resp = await FetchService.fetch<ObjSingleChange | ServerErrorDto>( const resp = await FetchService.fetch<ObjSingleChange | ServerErrorDto>(
`${this.storeUrl}/api/v1/obj/hash/${this.hash}`, `${this.storeUrl}/api/v1/obj/hash/${this.hash}`,
{ {
headers: this.getAuthHeaders(), headers: this.getAuthHeaders(),
method: 'GET' method: 'GET'
}, },
this.refreshParams() this.refreshParams(this.authUrl)
); );
// fnConsoleLog('ApiStoreObjGetByHashCommand->response', resp); // fnConsoleLog('ApiStoreObjGetByHashCommand->response', resp);
return resp.data; if (!resp.ok) throw new Error('PROBLEM !!!!!!!!!!!!!!!');
return { ...resp.data, sub: this.tokenData?.sub } as ObjSingleChangeSub;
} }
} }

View File

@ -27,7 +27,7 @@ export class ApiObjGetChangesCommand
extends ApiCallBase extends ApiCallBase
implements ICommand<Promise<ObjChangesResponse | ServerErrorDto>> implements ICommand<Promise<ObjChangesResponse | ServerErrorDto>>
{ {
constructor(private serverId: number) { constructor(private authUrl: string, private serverId: number) {
super(); super();
} }
async execute(): Promise<ObjChangesResponse | ServerErrorDto> { async execute(): Promise<ObjChangesResponse | ServerErrorDto> {
@ -37,7 +37,7 @@ export class ApiObjGetChangesCommand
const resp = await FetchService.fetch<ObjChangesResponse | ServerErrorDto>( const resp = await FetchService.fetch<ObjChangesResponse | ServerErrorDto>(
`${this.storeUrl}/api/v1/obj/changes?serverId=${this.serverId}`, `${this.storeUrl}/api/v1/obj/changes?serverId=${this.serverId}`,
{ headers: this.getAuthHeaders() }, { headers: this.getAuthHeaders() },
this.refreshParams() this.refreshParams(this.authUrl)
); );
if (resp.status === 200) return resp.data; if (resp.status === 200) return resp.data;
fnConsoleLog(resp); fnConsoleLog(resp);

View File

@ -21,7 +21,7 @@ import { ICommand } from '../../../../../common/model/shared/common.dto';
import { fnConsoleLog } from '../../../../../common/fn/fn-console'; import { fnConsoleLog } from '../../../../../common/fn/fn-console';
export class ApiObjGetChangesCommand extends ApiCallBase implements ICommand<Promise<ObjSingleChange | undefined>> { export class ApiObjGetChangesCommand extends ApiCallBase implements ICommand<Promise<ObjSingleChange | undefined>> {
constructor(private id: number) { constructor(private authUrl: string, private id: number) {
super(); super();
} }
async execute(): Promise<ObjSingleChange | undefined> { async execute(): Promise<ObjSingleChange | undefined> {
@ -31,7 +31,7 @@ export class ApiObjGetChangesCommand extends ApiCallBase implements ICommand<Pro
const resp = await FetchService.fetch<ObjSingleChange>( const resp = await FetchService.fetch<ObjSingleChange>(
`${this.storeUrl}/api/v1/obj/${this.id}`, `${this.storeUrl}/api/v1/obj/${this.id}`,
{ headers: this.getAuthHeaders() }, { headers: this.getAuthHeaders() },
this.refreshParams() this.refreshParams(this.authUrl)
); );
fnConsoleLog('ApiStoreChangesCommand->response', resp); fnConsoleLog('ApiStoreChangesCommand->response', resp);
return resp.data; return resp.data;

View File

@ -30,13 +30,13 @@ export interface FileDataDto {
} }
export class ApiSegmentAddCommand extends ApiCallBase implements ICommand<Promise<boolean>> { export class ApiSegmentAddCommand extends ApiCallBase implements ICommand<Promise<boolean>> {
constructor(private tx: BeginTxResponse, private file: string, private data: FileDataDto) { constructor(private authUrl: string, private tx: BeginTxResponse, private file: string, private data: FileDataDto) {
super(); super();
} }
async execute(): Promise<boolean> { async execute(): Promise<boolean> {
await this.initTokenData(); await this.initTokenData();
if (!this.storeUrl) return false; if (!this.storeUrl) return false;
// if (await this.hasSegment(this.storeUrl)) return true; if (await this.hasSegment(this.storeUrl)) return true;
return await this.addSegment(this.storeUrl); return await this.addSegment(this.storeUrl);
} }
@ -52,7 +52,7 @@ export class ApiSegmentAddCommand extends ApiCallBase implements ICommand<Promis
...authHeaders ...authHeaders
} }
}, },
this.refreshParams() this.refreshParams(this.authUrl)
); );
return resp.status === 200; return resp.status === 200;
} }
@ -99,7 +99,7 @@ export class ApiSegmentAddCommand extends ApiCallBase implements ICommand<Promis
method: 'POST', method: 'POST',
body: formData body: formData
}, },
this.refreshParams() this.refreshParams(this.authUrl)
); );
// fnConsoleLog('ApiSegmentAddCommand->resp', resp); // fnConsoleLog('ApiSegmentAddCommand->resp', resp);
return resp.status === 200; return resp.status === 200;

View File

@ -24,7 +24,7 @@ export class ApiSegmentGetChildrenCommand
extends ApiCallBase extends ApiCallBase
implements ICommand<Promise<SegmentHashListResponse | undefined>> implements ICommand<Promise<SegmentHashListResponse | undefined>>
{ {
constructor(private hash: string) { constructor(private authUrl: string, private hash: string) {
super(); super();
} }
async execute(): Promise<SegmentHashListResponse | undefined> { async execute(): Promise<SegmentHashListResponse | undefined> {
@ -34,7 +34,7 @@ export class ApiSegmentGetChildrenCommand
const resp = await FetchService.fetch<SegmentHashListResponse>( const resp = await FetchService.fetch<SegmentHashListResponse>(
`${this.storeUrl}/api/v1/segment/children/${this.hash}`, `${this.storeUrl}/api/v1/segment/children/${this.hash}`,
{ headers: this.getAuthHeaders() }, { headers: this.getAuthHeaders() },
this.refreshParams() this.refreshParams(this.authUrl)
); );
return resp.data; return resp.data;
} catch (e) { } catch (e) {

View File

@ -20,7 +20,7 @@ import { ICommand } from '../../../../../common/model/shared/common.dto';
import { fnConsoleLog } from '../../../../../common/fn/fn-console'; import { fnConsoleLog } from '../../../../../common/fn/fn-console';
export class ApiSegmentGetCommand extends ApiCallBase implements ICommand<Promise<Blob | undefined>> { export class ApiSegmentGetCommand extends ApiCallBase implements ICommand<Promise<Blob | undefined>> {
constructor(private hash: string, private mimeType?: string) { constructor(private authUrl: string, private hash: string, private mimeType?: string) {
super(); super();
} }
async execute(): Promise<Blob | undefined> { async execute(): Promise<Blob | undefined> {
@ -31,7 +31,7 @@ export class ApiSegmentGetCommand extends ApiCallBase implements ICommand<Promis
const resp = await FetchService.fetch<Blob>( const resp = await FetchService.fetch<Blob>(
`${this.storeUrl}/api/v1/segment/${this.hash}${mimeType}`, `${this.storeUrl}/api/v1/segment/${this.hash}${mimeType}`,
{ headers: this.getAuthHeaders(), type: 'BLOB' }, { headers: this.getAuthHeaders(), type: 'BLOB' },
this.refreshParams() this.refreshParams(this.authUrl)
); );
return resp.data; return resp.data;
} catch (e) { } catch (e) {

View File

@ -25,7 +25,7 @@ export class ApiSegmentQuotaGetCommand
extends ApiCallBase extends ApiCallBase
implements ICommand<Promise<ServerQuotaResponse | ServerErrorDto>> implements ICommand<Promise<ServerQuotaResponse | ServerErrorDto>>
{ {
constructor() { constructor(private authUrl: string) {
super(); super();
} }
async execute(): Promise<ServerQuotaResponse | ServerErrorDto> { async execute(): Promise<ServerQuotaResponse | ServerErrorDto> {
@ -38,7 +38,7 @@ export class ApiSegmentQuotaGetCommand
type: 'JSON', type: 'JSON',
headers: this.getAuthHeaders(true) headers: this.getAuthHeaders(true)
}, },
this.refreshParams() this.refreshParams(this.authUrl)
); );
return resp.data; return resp.data;
} catch (e) { } catch (e) {

View File

@ -0,0 +1,68 @@
/*
* This file is part of the pinmenote-extension distribution (https://github.com/pinmenote/pinmenote-extension).
* Copyright (c) 2023 Michal Szczepanski.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { FetchService, RefreshTokenParams, FetchHeaders } from '@pinmenote/fetch-service';
import { ICommand, ServerErrorDto } from '../../../common/model/shared/common.dto';
import { AccessTokenDto } from '../../../common/model/shared/token.dto';
import { TokenStorageSetCommand } from '../../../common/command/server/token/token-storage-set.command';
import { fnConsoleLog } from '../../../common/fn/fn-console';
import { ApiAuthUrlCommand } from '../api/api-auth-url.command';
export class ContentExtensionLoginCommand implements ICommand<Promise<void>> {
constructor(private token: AccessTokenDto) {}
async execute(): Promise<void> {
fnConsoleLog('ContentExtensionLoginCommand->execute');
await new ApiAuthUrlCommand().execute();
const baseUrl = await new ApiAuthUrlCommand().execute();
const req = await FetchService.fetch<AccessTokenDto | ServerErrorDto>(
`${baseUrl}/api/v1/login/new-device`,
{
method: 'PATCH',
headers: {
Authorization: `Bearer ${this.token.access_token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ source: 'EXTENSION' })
},
this.refreshParams(baseUrl)
);
if (req.ok && 'access_token' in req.data) {
await new TokenStorageSetCommand(req.data).execute();
}
}
protected refreshParams(baseUrl: string): RefreshTokenParams {
return {
data: {
refreshKey: 'message',
refreshValue: 'jwt expired',
method: 'PUT',
url: `${baseUrl}/api/v1/refresh-token`
},
successCallback: (res, headers) => {
const value: AccessTokenDto = JSON.parse(res.data);
fnConsoleLog('ContentExtensionLoginCommand->successCallback', res, 'value', value, 'headers', headers);
return {
...headers,
Authorization: `Bearer ${value.access_token}`
} as FetchHeaders;
},
errorCallback: (error) => {
fnConsoleLog('ContentExtensionLoginCommand->errorCallback', error);
}
};
}
}

View File

@ -23,7 +23,7 @@ import { BeginTxResponse } from '../../api/store/api-store.model';
import { SyncObjectStatus } from '../../../../common/model/sync.model'; import { SyncObjectStatus } from '../../../../common/model/sync.model';
export class SyncNoteCommand implements ICommand<Promise<SyncObjectStatus>> { export class SyncNoteCommand implements ICommand<Promise<SyncObjectStatus>> {
constructor(private obj: ObjDto<ObjNoteDto>, private tx: BeginTxResponse) {} constructor(private authUrl: string, private obj: ObjDto<ObjNoteDto>, private tx: BeginTxResponse) {}
// eslint-disable-next-line @typescript-eslint/require-await // eslint-disable-next-line @typescript-eslint/require-await
async execute(): Promise<SyncObjectStatus> { async execute(): Promise<SyncObjectStatus> {
fnConsoleLog('SyncNoteCommand'); fnConsoleLog('SyncNoteCommand');

View File

@ -14,25 +14,29 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { BrowserStorage } from '@pinmenote/browser-api';
import { ICommand, ServerErrorDto } from '../../../../common/model/shared/common.dto';
import { ObjDto } from '../../../../common/model/obj/obj.dto';
import { fnConsoleLog } from '../../../../common/fn/fn-console';
import { ApiObjAddCommand, ObjAddResponse } from '../../api/store/obj/api-obj-add.command'; import { ApiObjAddCommand, ObjAddResponse } from '../../api/store/obj/api-obj-add.command';
import { BeginTxResponse, ObjSingleChange } from '../../api/store/api-store.model'; import { ApiObjGetByHashCommand, ObjSingleChangeSub } from '../../api/store/obj/api-obj-get-by-hash.command';
import { ICommand, ServerErrorDto } from '../../../../common/model/shared/common.dto';
import { ApiErrorCode } from '../../../../common/model/shared/api.error-code'; import { ApiErrorCode } from '../../../../common/model/shared/api.error-code';
import { ApiObjGetByHashCommand } from '../../api/store/obj/api-obj-get-by-hash.command'; import { BeginTxResponse } from '../../api/store/api-store.model';
import { BrowserStorage } from '@pinmenote/browser-api';
import { ObjDto } from '../../../../common/model/obj/obj.dto';
import { ObjectStoreKeys } from '../../../../common/keys/object.store.keys'; import { ObjectStoreKeys } from '../../../../common/keys/object.store.keys';
import { fnSleep } from '../../../../common/fn/fn-sleep'; import { fnConsoleLog } from '../../../../common/fn/fn-console';
export class SyncObjectCommand implements ICommand<Promise<void>> { export class SyncObjectCommand implements ICommand<Promise<void>> {
constructor(private obj: ObjDto, private hash: string, private tx: BeginTxResponse) {} constructor(private authUrl: string, private obj: ObjDto, private hash: string, private tx: BeginTxResponse) {}
async execute(): Promise<void> { async execute(): Promise<void> {
if (this.obj.server?.id) return; if (this.obj.server?.id) return;
const resp: ObjAddResponse | ServerErrorDto = await new ApiObjAddCommand(this.obj, this.hash, this.tx).execute(); const resp: ObjAddResponse | ServerErrorDto = await new ApiObjAddCommand(
this.authUrl,
this.obj,
this.hash,
this.tx
).execute();
if ('serverId' in resp) { if ('serverId' in resp) {
return await this.saveServerId(resp.serverId); return await this.saveServerId(resp.serverId, resp.sub);
} else if ('code' in resp && resp.code === ApiErrorCode.SYNC_DUPLICATED_HASH) { } else if ('code' in resp && resp.code === ApiErrorCode.SYNC_DUPLICATED_HASH) {
return await this.setByHash(); return await this.setByHash();
} }
@ -41,19 +45,12 @@ export class SyncObjectCommand implements ICommand<Promise<void>> {
} }
private async setByHash(): Promise<void> { private async setByHash(): Promise<void> {
const resp: ObjSingleChange | ServerErrorDto = await new ApiObjGetByHashCommand(this.hash, this.tx).execute(); const resp: ObjSingleChangeSub = await new ApiObjGetByHashCommand(this.authUrl, this.hash).execute();
if ('serverId' in resp) { await this.saveServerId(resp.serverId, resp.sub);
await this.saveServerId(resp.serverId);
return;
}
fnConsoleLog('SyncObjectCommand->setByHash');
throw new Error('PROBLEM !!!!!!!!!!!!!!!');
} }
private async saveServerId(serverId: number): Promise<void> { private async saveServerId(serverId: number, sub: string): Promise<void> {
this.obj.server = { id: serverId }; this.obj.server = { id: serverId, sub };
await BrowserStorage.set(`${ObjectStoreKeys.OBJECT_ID}:${this.obj.id}`, this.obj); await BrowserStorage.set(`${ObjectStoreKeys.OBJECT_ID}:${this.obj.id}`, this.obj);
await BrowserStorage.set(`${ObjectStoreKeys.SERVER_ID}:${serverId}`, this.obj.id); await BrowserStorage.set(`${ObjectStoreKeys.SERVER_ID}:${serverId}`, this.obj.id);
await fnSleep(500);
fnConsoleLog('SyncObjectCommand->saveServerId', serverId, 'obj->id', this.obj.id);
} }
} }

View File

@ -27,11 +27,11 @@ import { ApiSegmentAddCommand } from '../../api/store/segment/api-segment-add.co
import { SyncCryptoFactory } from '../crypto/sync-crypto.factory'; import { SyncCryptoFactory } from '../crypto/sync-crypto.factory';
export class SyncPageNoteCommand implements ICommand<Promise<SyncObjectStatus>> { export class SyncPageNoteCommand implements ICommand<Promise<SyncObjectStatus>> {
constructor(private obj: ObjDto<ObjPageNoteDto>, private tx: BeginTxResponse) {} constructor(private authUrl: string, private obj: ObjDto<ObjPageNoteDto>, private tx: BeginTxResponse) {}
async execute(): Promise<SyncObjectStatus> { async execute(): Promise<SyncObjectStatus> {
const data = this.obj.data; const data = this.obj.data;
await new SyncObjectCommand(this.obj, data.hash, this.tx).execute(); await new SyncObjectCommand(this.authUrl, this.obj, data.hash, this.tx).execute();
await this.syncNote(data); await this.syncNote(data);
@ -40,7 +40,7 @@ export class SyncPageNoteCommand implements ICommand<Promise<SyncObjectStatus>>
private async syncNote(data: ObjPageNoteDto): Promise<void> { private async syncNote(data: ObjPageNoteDto): Promise<void> {
const content = JSON.stringify(data); const content = JSON.stringify(data);
await new ApiSegmentAddCommand(this.tx, content, { await new ApiSegmentAddCommand(this.authUrl, this.tx, content, {
key: await SyncCryptoFactory.newKey(), key: await SyncCryptoFactory.newKey(),
type: SyncHashType.ObjPdfDataDto, type: SyncHashType.ObjPdfDataDto,
hash: data.hash hash: data.hash

View File

@ -26,7 +26,7 @@ import { ApiSegmentAddCommand } from '../../api/store/segment/api-segment-add.co
import { SyncCryptoFactory } from '../crypto/sync-crypto.factory'; import { SyncCryptoFactory } from '../crypto/sync-crypto.factory';
export class SyncPdfCommand implements ICommand<Promise<SyncObjectStatus>> { export class SyncPdfCommand implements ICommand<Promise<SyncObjectStatus>> {
constructor(private obj: ObjDto<ObjPdfDto>, private tx: BeginTxResponse) {} constructor(private authUrl: string, private obj: ObjDto<ObjPdfDto>, private tx: BeginTxResponse) {}
async execute(): Promise<SyncObjectStatus> { async execute(): Promise<SyncObjectStatus> {
const data = this.obj.data; const data = this.obj.data;
await new SyncObjectCommand(this.obj, data.hash, this.tx).execute(); await new SyncObjectCommand(this.obj, data.hash, this.tx).execute();
@ -39,7 +39,7 @@ export class SyncPdfCommand implements ICommand<Promise<SyncObjectStatus>> {
private async syncData(data: ObjPdfDataDto, parent: string): Promise<void> { private async syncData(data: ObjPdfDataDto, parent: string): Promise<void> {
const content = JSON.stringify(data); const content = JSON.stringify(data);
await new ApiSegmentAddCommand(this.tx, content, { await new ApiSegmentAddCommand(this.authUrl, this.tx, content, {
key: await SyncCryptoFactory.newKey(), key: await SyncCryptoFactory.newKey(),
type: SyncHashType.ObjPdfDataDto, type: SyncHashType.ObjPdfDataDto,
hash: data.hash, hash: data.hash,
@ -50,7 +50,7 @@ export class SyncPdfCommand implements ICommand<Promise<SyncObjectStatus>> {
private async syncPdf(hash: string): Promise<void> { private async syncPdf(hash: string): Promise<void> {
const pdfData = await BrowserStorage.get<string | undefined>(`${ObjectStoreKeys.PDF_DATA}:${hash}`); const pdfData = await BrowserStorage.get<string | undefined>(`${ObjectStoreKeys.PDF_DATA}:${hash}`);
if (!pdfData) return; if (!pdfData) return;
await new ApiSegmentAddCommand(this.tx, pdfData, { await new ApiSegmentAddCommand(this.authUrl, this.tx, pdfData, {
key: await SyncCryptoFactory.newKey(), key: await SyncCryptoFactory.newKey(),
type: SyncHashType.ObjPdf, type: SyncHashType.ObjPdf,
hash hash

View File

@ -27,7 +27,7 @@ import { ObjVideoDataDto } from '../../../../common/model/obj/page-snapshot.dto'
import { SyncCryptoFactory } from '../crypto/sync-crypto.factory'; import { SyncCryptoFactory } from '../crypto/sync-crypto.factory';
export class SyncPinCommand implements ICommand<Promise<SyncObjectStatus>> { export class SyncPinCommand implements ICommand<Promise<SyncObjectStatus>> {
constructor(private obj: ObjDto<ObjPinDto>, private tx: BeginTxResponse) {} constructor(private authUrl: string, private obj: ObjDto<ObjPinDto>, private tx: BeginTxResponse) {}
async execute(): Promise<SyncObjectStatus> { async execute(): Promise<SyncObjectStatus> {
const data = this.obj.data; const data = this.obj.data;
await new SyncObjectCommand(this.obj, data.data.hash, this.tx).execute(); await new SyncObjectCommand(this.obj, data.data.hash, this.tx).execute();
@ -44,7 +44,7 @@ export class SyncPinCommand implements ICommand<Promise<SyncObjectStatus>> {
private syncPinVideo = async (parent: string, data?: ObjVideoDataDto) => { private syncPinVideo = async (parent: string, data?: ObjVideoDataDto) => {
if (!data) return; if (!data) return;
const content = JSON.stringify(data); const content = JSON.stringify(data);
await new ApiSegmentAddCommand(this.tx, content, { await new ApiSegmentAddCommand(this.authUrl, this.tx, content, {
key: await SyncCryptoFactory.newKey(), key: await SyncCryptoFactory.newKey(),
type: SyncHashType.ObjVideoDataDto, type: SyncHashType.ObjVideoDataDto,
hash: data.hash, hash: data.hash,
@ -56,7 +56,7 @@ export class SyncPinCommand implements ICommand<Promise<SyncObjectStatus>> {
for (const draw of data) { for (const draw of data) {
// TODO SYNC DRAW LIKE COMMENTS // TODO SYNC DRAW LIKE COMMENTS
const content = JSON.stringify(draw); const content = JSON.stringify(draw);
await new ApiSegmentAddCommand(this.tx, content, { await new ApiSegmentAddCommand(this.authUrl, this.tx, content, {
key: await SyncCryptoFactory.newKey(), key: await SyncCryptoFactory.newKey(),
type: SyncHashType.ObjDrawDto, type: SyncHashType.ObjDrawDto,
hash: draw.hash, hash: draw.hash,
@ -70,7 +70,7 @@ export class SyncPinCommand implements ICommand<Promise<SyncObjectStatus>> {
const comment = await new PinGetCommentCommand(hash).execute(); const comment = await new PinGetCommentCommand(hash).execute();
if (!comment) continue; if (!comment) continue;
const content = JSON.stringify(comment); const content = JSON.stringify(comment);
await new ApiSegmentAddCommand(this.tx, content, { await new ApiSegmentAddCommand(this.authUrl, this.tx, content, {
key: await SyncCryptoFactory.newKey(), key: await SyncCryptoFactory.newKey(),
type: SyncHashType.ObjCommentDto, type: SyncHashType.ObjCommentDto,
hash: comment.hash, hash: comment.hash,
@ -81,7 +81,7 @@ export class SyncPinCommand implements ICommand<Promise<SyncObjectStatus>> {
private syncPinData = async (data: ObjPinDataDto) => { private syncPinData = async (data: ObjPinDataDto) => {
const content = JSON.stringify(data); const content = JSON.stringify(data);
await new ApiSegmentAddCommand(this.tx, content, { await new ApiSegmentAddCommand(this.authUrl, this.tx, content, {
key: await SyncCryptoFactory.newKey(), key: await SyncCryptoFactory.newKey(),
type: SyncHashType.ObjPinDataDto, type: SyncHashType.ObjPinDataDto,
hash: data.hash hash: data.hash
@ -90,7 +90,7 @@ export class SyncPinCommand implements ICommand<Promise<SyncObjectStatus>> {
private syncPinDescription = async (data: ObjPinDescription, parent: string) => { private syncPinDescription = async (data: ObjPinDescription, parent: string) => {
const content = JSON.stringify(data); const content = JSON.stringify(data);
await new ApiSegmentAddCommand(this.tx, content, { await new ApiSegmentAddCommand(this.authUrl, this.tx, content, {
key: await SyncCryptoFactory.newKey(), key: await SyncCryptoFactory.newKey(),
type: SyncHashType.ObjPinDescription, type: SyncHashType.ObjPinDescription,
hash: data.hash, hash: data.hash,

View File

@ -14,14 +14,14 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { ObjDto, ObjRemovedDto } from '../../../../common/model/obj/obj.dto'; import { ObjRemovedDto } from '../../../../common/model/obj/obj.dto';
import { ICommand } from '../../../../common/model/shared/common.dto'; import { ICommand } from '../../../../common/model/shared/common.dto';
import { SyncObjectStatus } from '../../../../common/model/sync.model'; import { SyncObjectStatus } from '../../../../common/model/sync.model';
import { fnConsoleLog } from '../../../../common/fn/fn-console'; import { fnConsoleLog } from '../../../../common/fn/fn-console';
import { BeginTxResponse } from '../../api/store/api-store.model'; import { BeginTxResponse } from '../../api/store/api-store.model';
export class SyncRemovedCommand implements ICommand<Promise<SyncObjectStatus>> { export class SyncRemovedCommand implements ICommand<Promise<SyncObjectStatus>> {
constructor(private obj: ObjDto<ObjRemovedDto>, private tx: BeginTxResponse) {} constructor(private authUrl: string, private obj: ObjRemovedDto, private tx: BeginTxResponse) {}
// eslint-disable-next-line @typescript-eslint/require-await // eslint-disable-next-line @typescript-eslint/require-await
async execute(): Promise<SyncObjectStatus> { async execute(): Promise<SyncObjectStatus> {
fnConsoleLog('SyncRemovedCommand', this.obj, this.tx); fnConsoleLog('SyncRemovedCommand', this.obj, this.tx);

View File

@ -28,7 +28,7 @@ import { ApiSegmentAddCommand } from '../../api/store/segment/api-segment-add.co
import { SyncCryptoFactory } from '../crypto/sync-crypto.factory'; import { SyncCryptoFactory } from '../crypto/sync-crypto.factory';
export class SyncSnapshotCommand implements ICommand<Promise<SyncObjectStatus>> { export class SyncSnapshotCommand implements ICommand<Promise<SyncObjectStatus>> {
constructor(private obj: ObjDto<ObjPageDto>, private tx: BeginTxResponse) {} constructor(private authUrl: string, private obj: ObjDto<ObjPageDto>, private tx: BeginTxResponse) {}
async execute(): Promise<SyncObjectStatus> { async execute(): Promise<SyncObjectStatus> {
const page = this.obj.data; const page = this.obj.data;
const snapshot = page.snapshot; const snapshot = page.snapshot;
@ -54,7 +54,7 @@ export class SyncSnapshotCommand implements ICommand<Promise<SyncObjectStatus>>
if (!segment) return; if (!segment) return;
const content = this.getSegmentContent(segment); const content = this.getSegmentContent(segment);
if (!content) return; if (!content) return;
await new ApiSegmentAddCommand(this.tx, content, { await new ApiSegmentAddCommand(this.authUrl, this.tx, content, {
key: await SyncCryptoFactory.newKey(), key: await SyncCryptoFactory.newKey(),
type: SyncHashType.PageSnapshotFirstHash, type: SyncHashType.PageSnapshotFirstHash,
hash: segment.hash, hash: segment.hash,
@ -65,14 +65,14 @@ export class SyncSnapshotCommand implements ICommand<Promise<SyncObjectStatus>>
private async syncSnapshot(snapshot: PageSnapshotDto, parent: string): Promise<void> { private async syncSnapshot(snapshot: PageSnapshotDto, parent: string): Promise<void> {
// snapshot->info // snapshot->info
await new ApiSegmentAddCommand(this.tx, JSON.stringify(snapshot.info), { await new ApiSegmentAddCommand(this.authUrl, this.tx, JSON.stringify(snapshot.info), {
key: await SyncCryptoFactory.newKey(), key: await SyncCryptoFactory.newKey(),
type: SyncHashType.PageSnapshotInfoDto, type: SyncHashType.PageSnapshotInfoDto,
hash: snapshot.info.hash, hash: snapshot.info.hash,
parent parent
}).execute(); }).execute();
// snapshot->data // snapshot->data
await new ApiSegmentAddCommand(this.tx, JSON.stringify(snapshot.data), { await new ApiSegmentAddCommand(this.authUrl, this.tx, JSON.stringify(snapshot.data), {
key: await SyncCryptoFactory.newKey(), key: await SyncCryptoFactory.newKey(),
type: SyncHashType.PageSnapshotDataDto, type: SyncHashType.PageSnapshotDataDto,
hash: snapshot.data.hash, hash: snapshot.data.hash,
@ -90,7 +90,7 @@ export class SyncSnapshotCommand implements ICommand<Promise<SyncObjectStatus>>
const content = this.getSegmentContent(segment); const content = this.getSegmentContent(segment);
if (!content) return; if (!content) return;
await new ApiSegmentAddCommand(this.tx, content, { await new ApiSegmentAddCommand(this.authUrl, this.tx, content, {
key: await SyncCryptoFactory.newKey(), key: await SyncCryptoFactory.newKey(),
type: this.convertSegmentTypeSyncHashType(segment.type), type: this.convertSegmentTypeSyncHashType(segment.type),
hash, hash,

View File

@ -32,7 +32,7 @@ import { fnConsoleLog } from '../../../common/fn/fn-console';
import { BeginTxResponse } from '../api/store/api-store.model'; import { BeginTxResponse } from '../api/store/api-store.model';
export class SyncIndexCommand implements ICommand<Promise<SyncObjectStatus>> { export class SyncIndexCommand implements ICommand<Promise<SyncObjectStatus>> {
constructor(private tx: BeginTxResponse, private id: number) {} constructor(private authUrl: string, private tx: BeginTxResponse, private id: number) {}
async execute(): Promise<SyncObjectStatus> { async execute(): Promise<SyncObjectStatus> {
const obj = await new ObjGetCommand(this.id).execute(); const obj = await new ObjGetCommand(this.id).execute();
@ -46,22 +46,22 @@ export class SyncIndexCommand implements ICommand<Promise<SyncObjectStatus>> {
switch (obj.type) { switch (obj.type) {
case ObjTypeDto.PageSnapshot: case ObjTypeDto.PageSnapshot:
case ObjTypeDto.PageElementSnapshot: { case ObjTypeDto.PageElementSnapshot: {
return await new SyncSnapshotCommand(obj as ObjDto<ObjPageDto>, this.tx).execute(); return await new SyncSnapshotCommand(this.authUrl, obj as ObjDto<ObjPageDto>, this.tx).execute();
} }
case ObjTypeDto.PageElementPin: { case ObjTypeDto.PageElementPin: {
return await new SyncPinCommand(obj as ObjDto<ObjPinDto>, this.tx).execute(); return await new SyncPinCommand(this.authUrl, obj as ObjDto<ObjPinDto>, this.tx).execute();
} }
case ObjTypeDto.Pdf: { case ObjTypeDto.Pdf: {
return await new SyncPdfCommand(obj as ObjDto<ObjPdfDto>, this.tx).execute(); return await new SyncPdfCommand(this.authUrl, obj as ObjDto<ObjPdfDto>, this.tx).execute();
} }
case ObjTypeDto.Note: { case ObjTypeDto.Note: {
return await new SyncNoteCommand(obj as ObjDto<ObjNoteDto>, this.tx).execute(); return await new SyncNoteCommand(this.authUrl, obj as ObjDto<ObjNoteDto>, this.tx).execute();
} }
case ObjTypeDto.PageNote: { case ObjTypeDto.PageNote: {
return await new SyncPageNoteCommand(obj as ObjDto<ObjPageNoteDto>, this.tx).execute(); return await new SyncPageNoteCommand(this.authUrl, obj as ObjDto<ObjPageNoteDto>, this.tx).execute();
} }
case ObjTypeDto.Removed: { case ObjTypeDto.Removed: {
return await new SyncRemovedCommand(obj as ObjDto<ObjRemovedDto>, this.tx).execute(); return await new SyncRemovedCommand(this.authUrl, obj as any as ObjRemovedDto, this.tx).execute();
} }
default: { default: {
fnConsoleLog('SyncObjectCommand->PROBLEM', obj, 'index', this.id); fnConsoleLog('SyncObjectCommand->PROBLEM', obj, 'index', this.id);

View File

@ -23,6 +23,7 @@ import { fnConsoleLog } from '../../../common/fn/fn-console';
import { ObjDateIndex } from '../../../common/command/obj/index/obj-update-index-add.command'; import { ObjDateIndex } from '../../../common/command/obj/index/obj-update-index-add.command';
import { SyncSetProgressCommand } from './progress/sync-set-progress.command'; import { SyncSetProgressCommand } from './progress/sync-set-progress.command';
import { BrowserStorage } from '@pinmenote/browser-api'; import { BrowserStorage } from '@pinmenote/browser-api';
import { ApiAuthUrlCommand } from '../api/api-auth-url.command';
export class SyncMonthCommand implements ICommand<Promise<SyncObjectStatus>> { export class SyncMonthCommand implements ICommand<Promise<SyncObjectStatus>> {
constructor(private progress: SyncProgress, private yearMonth: string) {} constructor(private progress: SyncProgress, private yearMonth: string) {}
@ -51,16 +52,17 @@ export class SyncMonthCommand implements ICommand<Promise<SyncObjectStatus>> {
} }
async syncIndex(indexList: ObjDateIndex[], start: number): Promise<SyncObjectStatus> { async syncIndex(indexList: ObjDateIndex[], start: number): Promise<SyncObjectStatus> {
const begin = await SyncTxHelper.begin(); const authUrl = await new ApiAuthUrlCommand().execute();
const begin = await SyncTxHelper.begin(authUrl);
if (!begin) return SyncObjectStatus.TX_LOCKED; if (!begin) return SyncObjectStatus.TX_LOCKED;
let i = start; let i = start;
let status = SyncObjectStatus.OK; let status = SyncObjectStatus.OK;
for (i; i < indexList.length; i++) { for (i; i < indexList.length; i++) {
status = await new SyncIndexCommand(begin, indexList[i].id).execute(); status = await new SyncIndexCommand(authUrl, begin, indexList[i].id).execute();
switch (status) { switch (status) {
case SyncObjectStatus.SERVER_ERROR: { case SyncObjectStatus.SERVER_ERROR: {
fnConsoleLog('SERVER_ERROR !!!!!!!!!!!!!!!!!!!'); fnConsoleLog('SERVER_ERROR !!!!!!!!!!!!!!!!!!!');
await SyncTxHelper.commit(); await SyncTxHelper.commit(authUrl);
await this.updateProgress(indexList[i].id, indexList[i].dt); await this.updateProgress(indexList[i].id, indexList[i].dt);
return status; return status;
} }
@ -94,7 +96,7 @@ export class SyncMonthCommand implements ICommand<Promise<SyncObjectStatus>> {
// all conditions (not current year/month, not last id, object exists) are met // all conditions (not current year/month, not last id, object exists) are met
if (resetMonth) await this.updateProgress(-1, lastIndex.dt); if (resetMonth) await this.updateProgress(-1, lastIndex.dt);
await SyncTxHelper.commit(); await SyncTxHelper.commit(authUrl);
return SyncObjectStatus.OK; return SyncObjectStatus.OK;
} }

View File

@ -21,6 +21,7 @@ import { SyncObjIncomingCommand } from './incoming/sync-obj-incoming.command';
import { SyncSetProgressCommand } from './progress/sync-set-progress.command'; import { SyncSetProgressCommand } from './progress/sync-set-progress.command';
import { SyncProgress } from '../../../common/model/sync.model'; import { SyncProgress } from '../../../common/model/sync.model';
import { SwSyncStore } from '../../sw-sync.store'; import { SwSyncStore } from '../../sw-sync.store';
import { ApiAuthUrlCommand } from '../api/api-auth-url.command';
export class SyncServerIncomingCommand implements ICommand<Promise<void>> { export class SyncServerIncomingCommand implements ICommand<Promise<void>> {
constructor(private progress: SyncProgress) {} constructor(private progress: SyncProgress) {}
@ -28,7 +29,8 @@ export class SyncServerIncomingCommand implements ICommand<Promise<void>> {
if (SwSyncStore.isInSync) return; if (SwSyncStore.isInSync) return;
SwSyncStore.isInSync = true; SwSyncStore.isInSync = true;
try { try {
const changesResp = await new ApiObjGetChangesCommand(this.progress.serverId).execute(); const authUrl = await new ApiAuthUrlCommand().execute();
const changesResp = await new ApiObjGetChangesCommand(authUrl, this.progress.serverId).execute();
fnConsoleLog('SyncServerIncomingCommand->START', changesResp); fnConsoleLog('SyncServerIncomingCommand->START', changesResp);
if ('code' in changesResp) return; if ('code' in changesResp) return;
for (let i = 0; i < changesResp.data.length; i++) { for (let i = 0; i < changesResp.data.length; i++) {

View File

@ -21,6 +21,7 @@ import { fnConsoleLog } from '../../../common/fn/fn-console';
import { SyncResetProgressCommand } from './progress/sync-reset-progress.command'; import { SyncResetProgressCommand } from './progress/sync-reset-progress.command';
import { SyncServerIncomingCommand } from './sync-server-incoming.command'; import { SyncServerIncomingCommand } from './sync-server-incoming.command';
import { SwSyncStore } from '../../sw-sync.store'; import { SwSyncStore } from '../../sw-sync.store';
import { SyncServerOutgoingCommand } from './sync-server-outgoing.command';
export class SyncServerCommand implements ICommand<Promise<void>> { export class SyncServerCommand implements ICommand<Promise<void>> {
async execute(): Promise<void> { async execute(): Promise<void> {

View File

@ -28,10 +28,10 @@ import { TokenDataDto } from '../../../common/model/shared/token.dto';
const SYNC_DELAY = 10_000; const SYNC_DELAY = 10_000;
export class SyncTxHelper { export class SyncTxHelper {
static async begin(): Promise<BeginTxResponse | undefined> { static async begin(authUrl: string): Promise<BeginTxResponse | undefined> {
const tx = await BrowserStorage.get<BeginTxResponse | undefined>(ObjectStoreKeys.SYNC_TX); const tx = await BrowserStorage.get<BeginTxResponse | undefined>(ObjectStoreKeys.SYNC_TX);
if (tx) return tx; if (tx) return tx;
const txResponse = await new ApiStoreBeginCommand().execute(); const txResponse = await new ApiStoreBeginCommand(authUrl).execute();
if (txResponse?.locked) { if (txResponse?.locked) {
const token = await new TokenStorageGetCommand().execute(); const token = await new TokenStorageGetCommand().execute();
if (!token) return undefined; if (!token) return undefined;
@ -53,11 +53,11 @@ export class SyncTxHelper {
return txResponse; return txResponse;
} }
static async commit(): Promise<void> { static async commit(authUrl: string): Promise<void> {
const tx = await BrowserStorage.get<BeginTxResponse | undefined>(ObjectStoreKeys.SYNC_TX); const tx = await BrowserStorage.get<BeginTxResponse | undefined>(ObjectStoreKeys.SYNC_TX);
if (!tx) return; if (!tx) return;
fnConsoleLog('SyncServerCommand->commit', tx); fnConsoleLog('SyncServerCommand->commit', tx);
await new ApiStoreCommitCommand(tx).execute(); await new ApiStoreCommitCommand(authUrl, tx).execute();
await BrowserStorage.remove(ObjectStoreKeys.SYNC_TX); await BrowserStorage.remove(ObjectStoreKeys.SYNC_TX);
} }

View File

@ -40,6 +40,7 @@ import { fnConsoleLog } from '../common/fn/fn-console';
import { SyncManualOutgoingCommand } from './command/sync/manual/sync-manual-outgoing.command'; import { SyncManualOutgoingCommand } from './command/sync/manual/sync-manual-outgoing.command';
import { SyncServerIncomingCommand } from './command/sync/sync-server-incoming.command'; import { SyncServerIncomingCommand } from './command/sync/sync-server-incoming.command';
import { SyncGetProgressCommand } from './command/sync/progress/sync-get-progress.command'; import { SyncGetProgressCommand } from './command/sync/progress/sync-get-progress.command';
import { ContentExtensionLoginCommand } from './command/content/content-extension-login.command';
const handleMessage = async ( const handleMessage = async (
msg: BusMessage<any>, msg: BusMessage<any>,
@ -54,6 +55,9 @@ const handleMessage = async (
if (runtime.id !== BrowserApi.runtime.id) return; if (runtime.id !== BrowserApi.runtime.id) return;
switch (msg.type) { switch (msg.type) {
case BusMessageType.CONTENT_EXTENSION_LOGIN:
await new ContentExtensionLoginCommand(msg.data).execute();
break;
case BusMessageType.CONTENT_DOWNLOAD_DATA: case BusMessageType.CONTENT_DOWNLOAD_DATA:
await new ContentDownloadDataCommand(msg.data).execute(); await new ContentDownloadDataCommand(msg.data).execute();
break; break;