Compare commits

..

No commits in common. "develop" and "master" have entirely different histories.

17 changed files with 1 additions and 18257 deletions

View File

@ -1,11 +0,0 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "master",
"updateInternalDependencies": "patch",
"ignore": []
}

37
.gitignore vendored
View File

@ -1,37 +0,0 @@
# preset generated files
/.eslintrc.json
/.husky
/.jestrc.json
/.gitignore
/.lintstagedrc.json
/.npmignore
/.prettierrc.json
/tsconfig.json
/tsconfig.build.json
# dependency
node_modules
# output
/lib
/generated
# coverage
/coverage
*.lcov
# diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# log
/log
/logs
*.log
*-debug.log*
.*-debug.log*
# cache
.DS_Store
.cache
.npm
.pnpm*
*.tsbuildinfo
# configuration
.env*
!.env*.example
src/account/generated

View File

@ -1,25 +0,0 @@
{
"preset": [
"presetter-preset-esm"
],
"config": {
"prettier": {
"singleQuote": false
},
"tsconfig.build": {
"include": {
"0": "src"
},
"compilerOptions": {
"rootDir": "src"
}
},
"tsconfig": {
"compilerOptions": {
"lib": [
"dom"
]
}
}
}
}

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2024 Hammer Technologies LLC Copyright (c) 2024 LumeWeb
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

17236
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
import { defineConfig } from 'orval';
export default defineConfig({
account: {
input: './src/account/swagger.yaml',
output: {
mode: 'split',
workspace: "./src/account/generated",
target: 'openapi.ts',
override: {
mutator: {
path: '../axios.ts',
name: 'customInstance',
},
},
},
},
});

View File

@ -1,39 +0,0 @@
{
"name": "@lumeweb/portal-sdk",
"version": "0.0.0",
"type": "module",
"description": "",
"main": "lib/index.js",
"files": [
"lib"
],
"scripts": {
"prepare": "presetter bootstrap",
"build": "run build",
"postinstall": "patch-package",
"patch-package": "patch-package",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "gitea@git.lumeweb.com:LumeWeb/portal-sdk.git"
},
"private": false,
"devDependencies": {
"@changesets/cli": "^2.27.1",
"presetter": "^4.7.0",
"presetter-preset-esm": "^4.7.0"
},
"readme": "ERROR: No README data found!",
"_id": "@lumeweb/portal-sdk@0.0.0",
"dependencies": {
"@lumeweb/s5-js": "0.0.0-20240329003038",
"axios": "^1.6.7",
"memize": "^2.1.0",
"orval": "^6.25.0",
"patch-package": "^8.0.0"
},
"publishConfig": {
"access": "public"
}
}

View File

@ -1,15 +0,0 @@
diff --git a/node_modules/tsc-alias/dist/utils/import-path-resolver.js b/node_modules/tsc-alias/dist/utils/import-path-resolver.js
index ebaf620..227d4c7 100644
--- a/node_modules/tsc-alias/dist/utils/import-path-resolver.js
+++ b/node_modules/tsc-alias/dist/utils/import-path-resolver.js
@@ -5,8 +5,9 @@ const normalizePath = require("normalize-path");
const fs_1 = require("fs");
const path_1 = require("path");
const anyQuote = `["']`;
+const excludeLocalImportSyntax = `[(?!\.)]`;
const pathStringContent = `[^"'\r\n]+`;
-const importString = `(?:${anyQuote}${pathStringContent}${anyQuote})`;
+const importString = `(?:${anyQuote}${excludeLocalImportSyntax}${pathStringContent}${anyQuote})`;
const funcStyle = `(?:\\b(?:import|require)\\s*\\(\\s*(\\/\\*.*\\*\\/\\s*)?${importString}\\s*\\))`;
const globalStyle = `(?:\\bimport\\s+${importString})`;
const fromStyle = `(?:\\bfrom\\s+${importString})`;

View File

@ -1,357 +0,0 @@
import {
AccountInfoResponse,
getApiAccount,
LoginRequest,
LoginResponse,
OTPDisableRequest,
OTPGenerateResponse,
OTPValidateRequest,
OTPVerifyRequest,
PasswordResetVerifyRequest,
PingResponse,
postApiAccountPasswordResetRequest,
postApiAccountVerifyEmailResend,
postApiAuthPing,
RegisterRequest,
UploadLimitResponse,
VerifyEmailRequest,
} from "./account/generated/index.js";
import {
postApiAuthLogin,
postApiAuthRegister,
postApiAccountVerifyEmail,
getApiAuthOtpGenerate,
postApiAccountOtpVerify,
postApiAccountOtpValidate,
postApiAuthOtpDisable,
PasswordResetRequest,
postApiAccountPasswordResetConfirm,
postApiAuthLogout,
getApiUploadLimit,
postApiAccountUpdateEmail,
postApiAccountUpdatePassword,
} from "./account/generated/index.js";
import { AxiosError, AxiosResponse } from "axios";
export class AccountError extends Error {
public statusCode: number;
constructor(message: string, statusCode: number) {
super(message);
this.name = "AccountError";
this.statusCode = statusCode;
}
}
export class AccountApi {
private apiUrl: string;
private _jwtToken?: string;
constructor(apiUrl: string) {
let apiUrlParsed = new URL(apiUrl);
apiUrlParsed.hostname = `account.${apiUrlParsed.hostname}`;
this.apiUrl = apiUrlParsed.toString();
}
set jwtToken(value: string) {
this._jwtToken = value;
}
get jwtToken(): string {
return <string>this._jwtToken;
}
public static create(apiUrl: string): AccountApi {
return new AccountApi(apiUrl);
}
public async login(
loginRequest: LoginRequest,
): Promise<boolean | AccountError> {
let ret: AxiosResponse<LoginResponse> | LoginResponse | boolean = false;
try {
ret = await postApiAuthLogin(loginRequest, { baseURL: this.apiUrl });
} catch (e) {
return new AccountError(
(e as AxiosError).response?.data as string,
(e as AxiosError).response?.status as number,
);
}
ret = this.checkSuccessVal<LoginResponse>(ret);
if (ret) {
this._jwtToken = (ret as LoginResponse).token;
return true;
}
return false;
}
public async register(
registerRequest: RegisterRequest,
): Promise<boolean | Error> {
let ret: AxiosResponse<void>;
try {
ret = await postApiAuthRegister(registerRequest, {
baseURL: this.apiUrl,
});
} catch (e) {
return new AccountError(
(e as AxiosError).response?.data as string,
(e as AxiosError).response?.status as number,
);
}
return this.checkSuccessBool(ret);
}
public async verifyEmail(
verifyEmailRequest: VerifyEmailRequest,
): Promise<boolean | Error> {
let ret: AxiosResponse<void>;
try {
ret = await postApiAccountVerifyEmail(
verifyEmailRequest,
this.buildOptions(),
);
} catch (e) {
return new AccountError(
(e as AxiosError).response?.data as string,
(e as AxiosError).response?.status as number,
);
}
return this.checkSuccessBool(ret);
}
public async requestEmailVerification(): Promise<boolean | AccountError> {
let ret: AxiosResponse<void>;
try {
ret = await postApiAccountVerifyEmailResend(this.buildOptions());
} catch (e) {
return new AccountError(
(e as AxiosError).response?.data as string,
(e as AxiosError).response?.status as number,
);
}
return this.checkSuccessBool(ret);
}
public async generateOtp(): Promise<
boolean | OTPGenerateResponse | AccountError
> {
let ret: AxiosResponse<OTPGenerateResponse>;
try {
ret = await getApiAuthOtpGenerate(this.buildOptions());
} catch (e) {
return new AccountError(
(e as AxiosError).response?.data as string,
(e as AxiosError).response?.status as number,
);
}
return this.checkSuccessVal<OTPGenerateResponse>(ret);
}
public async verifyOtp(
otpVerifyRequest: OTPVerifyRequest,
): Promise<boolean | Error> {
let ret: AxiosResponse<void>;
try {
ret = await postApiAccountOtpVerify(
otpVerifyRequest,
this.buildOptions(),
);
} catch (e) {
return new AccountError(
(e as AxiosError).response?.data as string,
(e as AxiosError).response?.status as number,
);
}
return this.checkSuccessBool(ret);
}
public async validateOtp(
otpValidateRequest: OTPValidateRequest,
): Promise<boolean | AccountError> {
let ret: AxiosResponse<void>;
try {
ret = await postApiAccountOtpValidate(
otpValidateRequest,
this.buildOptions(),
);
} catch (e) {
return new AccountError(
(e as AxiosError).response?.data as string,
(e as AxiosError).response?.status as number,
);
}
return this.checkSuccessBool(ret);
}
public async disableOtp(
otpDisableRequest: OTPDisableRequest,
): Promise<boolean | AccountError> {
let ret: AxiosResponse<void>;
try {
ret = await postApiAuthOtpDisable(otpDisableRequest, this.buildOptions());
} catch (e) {
return new AccountError(
(e as AxiosError).response?.data as string,
(e as AxiosError).response?.status as number,
);
}
return this.checkSuccessBool(ret);
}
public async requestPasswordReset(
passwordResetRequest: PasswordResetRequest,
): Promise<boolean | AccountError> {
let ret: AxiosResponse<void>;
try {
ret = await postApiAccountPasswordResetRequest(
passwordResetRequest,
this.buildOptions(),
);
} catch (e) {
return new AccountError(
(e as AxiosError).response?.data as string,
(e as AxiosError).response?.status as number,
);
}
return this.checkSuccessBool(ret);
}
public async confirmPasswordReset(
passwordResetVerifyRequest: PasswordResetVerifyRequest,
): Promise<boolean | AccountError> {
let ret: AxiosResponse<void>;
try {
ret = await postApiAccountPasswordResetConfirm(
passwordResetVerifyRequest,
this.buildOptions(),
);
} catch (e) {
return new AccountError(
(e as AxiosError).response?.data as string,
(e as AxiosError).response?.status as number,
);
}
return this.checkSuccessBool(ret);
}
public async ping(): Promise<boolean> {
let ret: AxiosResponse<PingResponse>;
try {
ret = await postApiAuthPing(this.buildOptions());
} catch (e) {
return false;
}
const success = this.checkSuccessVal(ret) && ret.data.ping == "pong";
if (success) {
this._jwtToken = ret.data.token;
}
return success;
}
public async info(): Promise<boolean | AccountInfoResponse> {
let ret: AxiosResponse<AccountInfoResponse>;
try {
ret = await getApiAccount(this.buildOptions());
} catch (e) {
return false;
}
return this.checkSuccessVal(ret);
}
public async logout(): Promise<boolean> {
try {
await postApiAuthLogout(this.buildOptions());
} catch (e) {
return false;
}
this._jwtToken = undefined;
return true;
}
public async uploadLimit(): Promise<number> {
let ret: AxiosResponse<UploadLimitResponse>;
try {
ret = await getApiUploadLimit(this.buildOptions());
} catch (e) {
return 0;
}
return this.checkSuccessVal<UploadLimitResponse>(ret) ? ret.data.limit : 0;
}
public async updateEmail(
email: string,
password: string,
): Promise<boolean | AccountError> {
let ret: AxiosResponse<void>;
try {
ret = await postApiAccountUpdateEmail(
{ email, password },
this.buildOptions(),
);
} catch (e) {
return new AccountError(
(e as AxiosError).response?.data as string,
(e as AxiosError).response?.status as number,
);
}
return this.checkSuccessBool(ret);
}
public async updatePassword(
currentPassword: string,
newPassword: string,
): Promise<boolean | AccountError> {
let ret: AxiosResponse<void>;
try {
ret = await postApiAccountUpdatePassword(
{ current_password: currentPassword, new_password: newPassword },
this.buildOptions(),
);
} catch (e) {
return new AccountError(
(e as AxiosError).response?.data as string,
(e as AxiosError).response?.status as number,
);
}
return this.checkSuccessBool(ret);
}
private checkSuccessBool(ret: AxiosResponse<void>): boolean {
return ret.status === 200;
}
private checkSuccessVal<T>(ret: AxiosResponse<T>): T | boolean {
if (ret.status === 200) {
return ret.data as T;
}
return false;
}
private buildOptions(): any {
const headers: any = {};
if (this.jwtToken) {
headers.Authorization = `Bearer ${this.jwtToken}`;
}
return {
baseURL: this.apiUrl,
headers: headers,
};
}
}

View File

@ -1,25 +0,0 @@
import Axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import memize from "memize";
const axiosCreate = memize(Axios.create);
export const customInstance = <T>(
config: AxiosRequestConfig,
options?: AxiosRequestConfig,
): Promise<AxiosResponse<T>> => {
const source = Axios.CancelToken.source();
const instance = axiosCreate({ baseURL: options?.baseURL });
const promise = instance({
...config,
...options,
cancelToken: source.token,
});
// @ts-ignore
promise.cancel = () => {
source.cancel("Query was cancelled");
};
return promise;
};

View File

@ -1,350 +0,0 @@
openapi: 3.0.0
info:
title: Account Management API
version: "1.0"
description: API for managing user accounts, including login, registration, OTP operations, and password resets.
paths:
/api/auth/login:
post:
summary: Login to the system
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/LoginRequest"
responses:
"200":
description: Successfully logged in
content:
application/json:
schema:
$ref: "#/components/schemas/LoginResponse"
"401":
description: Unauthorized
/api/auth/logout:
post:
summary: Logout of account service
responses:
"200":
description: Successfully logged out
/api/auth/register:
post:
summary: Register a new account
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/RegisterRequest"
responses:
"200":
description: Successfully registered
"400":
description: Bad Request
/api/account/verify-email:
post:
summary: Verify email address
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/VerifyEmailRequest"
responses:
"200":
description: Email verified successfully
/api/account/verify-email/resend:
post:
summary: Resend email verification
responses:
"200":
description: Email verification resent successfully
/api/auth/otp/generate:
get:
summary: Generate OTP for two-factor authentication
responses:
"200":
description: OTP generated successfully
content:
application/json:
schema:
$ref: "#/components/schemas/OTPGenerateResponse"
/api/account/otp/verify:
post:
summary: Verify OTP for enabling two-factor authentication
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/OTPVerifyRequest"
responses:
"200":
description: OTP verified successfully
/api/account/otp/validate:
post:
summary: Validate OTP for two-factor authentication login
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/OTPValidateRequest"
responses:
"200":
description: OTP validated successfully
/api/auth/otp/disable:
post:
summary: Disable OTP for two-factor authentication
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/OTPDisableRequest"
responses:
"200":
description: OTP disabled successfully
/api/account/password-reset/request:
post:
summary: Request a password reset
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PasswordResetRequest"
responses:
"200":
description: Password reset requested successfully
/api/account/password-reset/confirm:
post:
summary: Confirm a password reset
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PasswordResetVerifyRequest"
responses:
"200":
description: Password reset successfully
/api/auth/ping:
post:
summary: Auth check endpoint
responses:
"200":
description: Pong
content:
application/json:
schema:
$ref: "#/components/schemas/PingResponse"
"401":
description: Unauthorized
/api/account:
get:
summary: Get account information
responses:
"200":
description: Account information retrieved successfully
content:
application/json:
schema:
$ref: "#/components/schemas/AccountInfoResponse"
"401":
description: Unauthorized
/api/account/update-email:
post:
summary: Update email address
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateEmailRequest"
responses:
"200":
description: Email updated successfully
/api/account/update-password:
post:
summary: Update password
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdatePasswordRequest"
responses:
"200":
description: Password updated successfully
/api/upload-limit:
get:
summary: Get the basic file upload (POST) upload limit set by the portal
responses:
"200":
description: Upload limit retrieved successfully
content:
application/json:
schema:
$ref: "#/components/schemas/UploadLimitResponse"
/api/meta:
get:
summary: Get metadata about the portal
responses:
"200":
description: Metadata retrieved successfully
content:
application/json:
schema:
$ref: "#/components/schemas/MetaResponse"
components:
schemas:
LoginRequest:
type: object
required:
- email
- password
properties:
email:
type: string
password:
type: string
LoginResponse:
type: object
properties:
token:
type: string
RegisterRequest:
type: object
required:
- first_name
- last_name
- email
- password
properties:
first_name:
type: string
last_name:
type: string
email:
type: string
password:
type: string
VerifyEmailRequest:
type: object
required:
- email
- token
properties:
email:
type: string
token:
type: string
OTPGenerateResponse:
type: object
properties:
OTP:
type: string
OTPVerifyRequest:
type: object
required:
- OTP
properties:
OTP:
type: string
OTPValidateRequest:
type: object
required:
- OTP
properties:
OTP:
type: string
OTPDisableRequest:
type: object
required:
- password
properties:
password:
type: string
PasswordResetRequest:
type: object
required:
- email
properties:
email:
type: string
PasswordResetVerifyRequest:
type: object
required:
- email
- token
- password
properties:
email:
type: string
token:
type: string
password:
type: string
UpdateEmailRequest:
type: object
required:
- email
- password
properties:
email:
type: string
password:
type: string
UpdatePasswordRequest:
type: object
required:
- current_password
- new_password
properties:
current_password:
type: string
new_password:
type: string
PingResponse:
type: object
properties:
ping:
type: string
token:
type: string
AccountInfoResponse:
type: object
required:
- id
- first_name
- last_name
- email
- verified
properties:
id:
type: number
first_name:
type: string
last_name:
type: string
email:
type: string
verified:
type: boolean
UploadLimitResponse:
type: object
properties:
limit:
type: number
required:
- limit
MetaResponse:
type: object
required:
- domain
properties:
domain:
type: string

View File

@ -1,4 +0,0 @@
export * from "./sdk.js";
export * from "./account/generated/openapi.schemas.js";
export * from "./protocol/index.js";
export { AccountError } from "./account.js";

View File

@ -1,24 +0,0 @@
export abstract class Protocol<T> {
private sdk: T;
constructor(sdk: T) {
this.sdk = sdk;
}
public getSdk(): T {
return this.sdk;
}
public abstract setAuthToken(token: string): Promise<void>;
}
export interface ProtocolConstructor<T> {
new (apiDomain: string): Protocol<T>;
}
export function createProtocol<T>(
implementation: ProtocolConstructor<T>,
apiDomain: string,
): Protocol<T> {
return new implementation(apiDomain);
}

View File

@ -1,18 +0,0 @@
import { Protocol } from "./protocol";
import { S5Client } from "@lumeweb/s5-js";
export const PROTOCOL_S5 = "s5";
export class S5 extends Protocol<S5Client> {
constructor(apiDomain: string) {
const sdk = new S5Client(`s5.${apiDomain}`);
super(sdk);
}
async setAuthToken(token: string): Promise<void> {
const options = this.getSdk().clientOptions;
options.apiKey = token;
this.getSdk().clientOptions = options;
}
}

View File

@ -1,14 +0,0 @@
import { Registry } from "./registry";
import { createProtocol, Protocol } from "./impl/protocol.js";
import { S5Client } from "@lumeweb/s5-js";
import { PROTOCOL_S5, S5 } from "./impl/s5.js";
export function registerDefaults(registry: Registry) {
registry.register<S5Client>(
PROTOCOL_S5,
createProtocol<S5Client>(S5, registry.getApiDomain()),
);
}
export { Protocol, Registry };
export { PROTOCOL_S5 };

View File

@ -1,34 +0,0 @@
import { Sdk } from "../sdk";
import { Protocol } from "./impl/protocol";
export class Registry {
private store: Map<string, Protocol<any>>;
private sdk: Sdk;
private _apiDomain?: string;
constructor(sdk: Sdk) {
this.store = new Map();
this.sdk = sdk;
}
public register<T>(name: string, value: Protocol<T>) {
this.store.set(name, value);
}
public get<T>(name: string): Protocol<T> {
return this.store.get(name) as Protocol<T>;
}
[Symbol.iterator]() {
return this.store[Symbol.iterator]();
}
public getApiDomain(): string {
if (!this._apiDomain) {
const urlObject = new URL(this.sdk.apiUrl);
this._apiDomain = urlObject.hostname;
}
return this._apiDomain;
}
}

View File

@ -1,49 +0,0 @@
import { AccountApi } from "./account.js";
import { registerDefaults, Registry } from "./protocol/index.js";
export class Sdk {
private accountApi?: AccountApi;
private registry?: Registry;
constructor(apiUrl: string) {
this._apiUrl = apiUrl;
}
private _apiUrl: string;
get apiUrl(): string {
return this._apiUrl;
}
set apiUrl(value: string) {
this._apiUrl = value;
}
public static create(apiUrl: string): Sdk {
return new Sdk(apiUrl);
}
public account(): AccountApi {
if (!this.accountApi) {
this.accountApi = AccountApi.create(this._apiUrl);
}
return this.accountApi!;
}
public protocols(): Registry {
if (!this.registry) {
this.registry = new Registry(this);
registerDefaults(this.registry!);
}
return this.registry!;
}
public setAuthToken(token: string) {
this.account().jwtToken = token;
for (const [, protocol] of this.protocols()) {
protocol.setAuthToken(token);
}
}
}