Add StateVersion.Three to fix premium migration (#666)
* Add StateVersion.Three to fix premium migration
This commit is contained in:
parent
52f77c0277
commit
5fad7c666f
|
@ -1,5 +1,6 @@
|
||||||
export enum StateVersion {
|
export enum StateVersion {
|
||||||
One = 1, // Original flat key/value pair store
|
One = 1, // Original flat key/value pair store
|
||||||
Two = 2, // Move to a typed State object
|
Two = 2, // Move to a typed State object
|
||||||
Latest = Two,
|
Three = 3, // Fix migration of users' premium status
|
||||||
|
Latest = Three,
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@ import { GlobalStateFactory } from "../factories/globalStateFactory";
|
||||||
import { StateFactory } from "../factories/stateFactory";
|
import { StateFactory } from "../factories/stateFactory";
|
||||||
import { Account, AccountSettings } from "../models/domain/account";
|
import { Account, AccountSettings } from "../models/domain/account";
|
||||||
|
|
||||||
|
import { TokenService } from "./token.service";
|
||||||
|
|
||||||
// Originally (before January 2022) storage was handled as a flat key/value pair store.
|
// Originally (before January 2022) storage was handled as a flat key/value pair store.
|
||||||
// With the move to a typed object for state storage these keys should no longer be in use anywhere outside of this migration.
|
// With the move to a typed object for state storage these keys should no longer be in use anywhere outside of this migration.
|
||||||
const v1Keys: { [key: string]: string } = {
|
const v1Keys: { [key: string]: string } = {
|
||||||
|
@ -153,6 +155,9 @@ export class StateMigrationService<
|
||||||
case StateVersion.One:
|
case StateVersion.One:
|
||||||
await this.migrateStateFrom1To2();
|
await this.migrateStateFrom1To2();
|
||||||
break;
|
break;
|
||||||
|
case StateVersion.Two:
|
||||||
|
await this.migrateStateFrom2To3();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentStateVersion += 1;
|
currentStateVersion += 1;
|
||||||
|
@ -448,6 +453,27 @@ export class StateMigrationService<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async migrateStateFrom2To3(): Promise<void> {
|
||||||
|
const authenticatedUserIds = await this.get<string[]>(keys.authenticatedAccounts);
|
||||||
|
await Promise.all(
|
||||||
|
authenticatedUserIds.map(async (userId) => {
|
||||||
|
const account = await this.get<TAccount>(userId);
|
||||||
|
if (
|
||||||
|
account?.profile?.hasPremiumPersonally === null &&
|
||||||
|
account.tokens?.accessToken != null
|
||||||
|
) {
|
||||||
|
const decodedToken = await TokenService.decodeToken(account.tokens.accessToken);
|
||||||
|
account.profile.hasPremiumPersonally = decodedToken.premium;
|
||||||
|
await this.set(userId, account);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const globals = await this.getGlobals();
|
||||||
|
globals.stateVersion = StateVersion.Three;
|
||||||
|
await this.set(keys.global, globals);
|
||||||
|
}
|
||||||
|
|
||||||
protected get options(): StorageOptions {
|
protected get options(): StorageOptions {
|
||||||
return { htmlStorageLocation: HtmlStorageLocation.Local };
|
return { htmlStorageLocation: HtmlStorageLocation.Local };
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,25 @@ import { Utils } from "../misc/utils";
|
||||||
import { IdentityTokenResponse } from "../models/response/identityTokenResponse";
|
import { IdentityTokenResponse } from "../models/response/identityTokenResponse";
|
||||||
|
|
||||||
export class TokenService implements TokenServiceAbstraction {
|
export class TokenService implements TokenServiceAbstraction {
|
||||||
|
static decodeToken(token: string): Promise<any> {
|
||||||
|
if (token == null) {
|
||||||
|
throw new Error("Token not provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = token.split(".");
|
||||||
|
if (parts.length !== 3) {
|
||||||
|
throw new Error("JWT must have 3 parts");
|
||||||
|
}
|
||||||
|
|
||||||
|
const decoded = Utils.fromUrlB64ToUtf8(parts[1]);
|
||||||
|
if (decoded == null) {
|
||||||
|
throw new Error("Cannot decode the token");
|
||||||
|
}
|
||||||
|
|
||||||
|
const decodedToken = JSON.parse(decoded);
|
||||||
|
return decodedToken;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(private stateService: StateService) {}
|
constructor(private stateService: StateService) {}
|
||||||
|
|
||||||
async setTokens(
|
async setTokens(
|
||||||
|
@ -87,18 +106,7 @@ export class TokenService implements TokenServiceAbstraction {
|
||||||
throw new Error("Token not found.");
|
throw new Error("Token not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const parts = token.split(".");
|
return TokenService.decodeToken(token);
|
||||||
if (parts.length !== 3) {
|
|
||||||
throw new Error("JWT must have 3 parts");
|
|
||||||
}
|
|
||||||
|
|
||||||
const decoded = Utils.fromUrlB64ToUtf8(parts[1]);
|
|
||||||
if (decoded == null) {
|
|
||||||
throw new Error("Cannot decode the token");
|
|
||||||
}
|
|
||||||
|
|
||||||
const decodedToken = JSON.parse(decoded);
|
|
||||||
return decodedToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTokenExpirationDate(): Promise<Date> {
|
async getTokenExpirationDate(): Promise<Date> {
|
||||||
|
|
Loading…
Reference in New Issue