mirror of
https://github.com/logto-io/logto.git
synced 2025-01-13 21:30:30 -05:00
feat(core): remove redundancy and reorganize code
This commit is contained in:
parent
715e77ec09
commit
0ae081ad56
9 changed files with 12 additions and 194 deletions
|
@ -1,20 +0,0 @@
|
||||||
import type { Config } from '@jest/types';
|
|
||||||
|
|
||||||
const config: Config.InitialOptions = {
|
|
||||||
preset: 'ts-jest',
|
|
||||||
testEnvironment: 'node',
|
|
||||||
moduleNameMapper: {
|
|
||||||
'^@/(.*)$': '<rootDir>/src/$1',
|
|
||||||
'^jose/(.*)$': '<rootDir>/node_modules/jose/dist/node/cjs/$1',
|
|
||||||
},
|
|
||||||
setupFilesAfterEnv: ['jest-matcher-specific-error'],
|
|
||||||
coveragePathIgnorePatterns: ['/node_modules/', '/build/'],
|
|
||||||
coverageReporters: ['text-summary', 'lcov'],
|
|
||||||
globals: {
|
|
||||||
'ts-jest': {
|
|
||||||
tsconfig: 'tsconfig.test.json',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
|
@ -14,9 +14,7 @@
|
||||||
"build": "rm -rf lib/ && tsc --p tsconfig.build.json",
|
"build": "rm -rf lib/ && tsc --p tsconfig.build.json",
|
||||||
"lint": "eslint --ext .ts src",
|
"lint": "eslint --ext .ts src",
|
||||||
"lint:report": "pnpm lint -- --format json --output-file report.json",
|
"lint:report": "pnpm lint -- --format json --output-file report.json",
|
||||||
"prepack": "pnpm build",
|
"prepack": "pnpm build"
|
||||||
"test": "jest",
|
|
||||||
"test:coverage": "jest --coverage --silent"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.0.0"
|
"node": "^16.0.0"
|
||||||
|
@ -24,8 +22,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@logto/phrases": "^0.1.0",
|
"@logto/phrases": "^0.1.0",
|
||||||
"@logto/schemas": "^0.1.0",
|
"@logto/schemas": "^0.1.0",
|
||||||
"got": "^11.8.2",
|
"got": "^11.8.2"
|
||||||
"jose": "^3.14.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/types": "^27.5.1",
|
"@jest/types": "^27.5.1",
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
import got from 'got';
|
|
||||||
|
|
||||||
import { getSignature, request } from './aliyun';
|
|
||||||
|
|
||||||
jest.mock('got');
|
|
||||||
describe('getSignature', () => {
|
|
||||||
it('should get valid signature', () => {
|
|
||||||
const parameters = {
|
|
||||||
AccessKeyId: 'testid',
|
|
||||||
AccountName: "<a%b'>",
|
|
||||||
Action: 'SingleSendMail',
|
|
||||||
AddressType: '1',
|
|
||||||
Format: 'XML',
|
|
||||||
HtmlBody: '4',
|
|
||||||
RegionId: 'cn-hangzhou',
|
|
||||||
ReplyToAddress: 'true',
|
|
||||||
SignatureMethod: 'HMAC-SHA1',
|
|
||||||
SignatureNonce: 'c1b2c332-4cfb-4a0f-b8cc-ebe622aa0a5c',
|
|
||||||
SignatureVersion: '1.0',
|
|
||||||
Subject: '3',
|
|
||||||
TagName: '2',
|
|
||||||
Timestamp: '2016-10-20T06:27:56Z',
|
|
||||||
ToAddress: '1@test.com',
|
|
||||||
Version: '2015-11-23',
|
|
||||||
};
|
|
||||||
const signature = getSignature(parameters, 'testsecret', 'POST');
|
|
||||||
expect(signature).toEqual('llJfXJjBW3OacrVgxxsITgYaYm0=');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('request', () => {
|
|
||||||
it('should call axios.post with extended params', async () => {
|
|
||||||
const parameters = {
|
|
||||||
AccessKeyId: 'testid',
|
|
||||||
AccountName: "<a%b'>",
|
|
||||||
Action: 'SingleSendMail',
|
|
||||||
AddressType: '1',
|
|
||||||
Format: 'XML',
|
|
||||||
HtmlBody: '4',
|
|
||||||
RegionId: 'cn-hangzhou',
|
|
||||||
ReplyToAddress: 'true',
|
|
||||||
Subject: '3',
|
|
||||||
TagName: '2',
|
|
||||||
ToAddress: '1@test.com',
|
|
||||||
Version: '2015-11-23',
|
|
||||||
SignatureMethod: 'HMAC-SHA1',
|
|
||||||
SignatureVersion: '1.0',
|
|
||||||
};
|
|
||||||
await request('http://test.endpoint.com', parameters, 'testsecret');
|
|
||||||
const calledData = (got.post as jest.MockedFunction<typeof got.post>).mock.calls[0];
|
|
||||||
expect(calledData).not.toBeUndefined();
|
|
||||||
const payload = calledData?.[0].form as URLSearchParams;
|
|
||||||
|
|
||||||
expect(payload.get('AccessKeyId')).toEqual('testid');
|
|
||||||
expect(payload.get('Timestamp')).not.toBeNull();
|
|
||||||
expect(payload.get('SignatureNonce')).not.toBeNull();
|
|
||||||
expect(payload.get('Signature')).not.toBeNull();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,83 +0,0 @@
|
||||||
import { createHmac } from 'crypto';
|
|
||||||
|
|
||||||
import { has } from '@silverhand/essentials';
|
|
||||||
import got from 'got';
|
|
||||||
|
|
||||||
export type { Response } from 'got';
|
|
||||||
|
|
||||||
// Aliyun has special escape rules.
|
|
||||||
// https://help.aliyun.com/document_detail/29442.html
|
|
||||||
const escaper = (string_: string) =>
|
|
||||||
encodeURIComponent(string_)
|
|
||||||
.replace(/\*/g, '%2A')
|
|
||||||
.replace(/'/g, '%27')
|
|
||||||
.replace(/!/g, '%21')
|
|
||||||
.replace(/"/g, '%22')
|
|
||||||
.replace(/\(/g, '%28')
|
|
||||||
.replace(/\)/g, '%29')
|
|
||||||
.replace(/\+/, '%2B');
|
|
||||||
|
|
||||||
export const getSignature = (
|
|
||||||
parameters: Record<string, string>,
|
|
||||||
secret: string,
|
|
||||||
method: string
|
|
||||||
) => {
|
|
||||||
const canonicalizedQuery = Object.keys(parameters)
|
|
||||||
.slice()
|
|
||||||
.sort()
|
|
||||||
.map((key) => {
|
|
||||||
const value = parameters[key];
|
|
||||||
|
|
||||||
return value === undefined ? '' : `${escaper(key)}=${escaper(value)}`;
|
|
||||||
})
|
|
||||||
.filter(Boolean)
|
|
||||||
.join('&');
|
|
||||||
const stringToSign = `${method.toUpperCase()}&${escaper('/')}&${escaper(canonicalizedQuery)}`;
|
|
||||||
|
|
||||||
return createHmac('sha1', `${secret}&`).update(stringToSign).digest('base64');
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface PublicParameters {
|
|
||||||
AccessKeyId: string;
|
|
||||||
Format?: string; // 'json' or 'xml', default: 'json'
|
|
||||||
RegionId?: string; // 'cn-hangzhou' | 'ap-southeast-1' | 'ap-southeast-2'
|
|
||||||
Signature?: string;
|
|
||||||
SignatureMethod?: string;
|
|
||||||
SignatureNonce?: string;
|
|
||||||
SignatureVersion?: string;
|
|
||||||
Timestamp?: string;
|
|
||||||
Version?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const request = async <T>(
|
|
||||||
url: string,
|
|
||||||
parameters: PublicParameters & Record<string, string>,
|
|
||||||
accessKeySecret: string
|
|
||||||
) => {
|
|
||||||
const finalParameters: Record<string, string> = {
|
|
||||||
...parameters,
|
|
||||||
SignatureNonce: String(Math.random()),
|
|
||||||
Timestamp: new Date().toISOString(),
|
|
||||||
};
|
|
||||||
const signature = getSignature(finalParameters, accessKeySecret, 'POST');
|
|
||||||
const payload = new URLSearchParams();
|
|
||||||
|
|
||||||
for (const key in finalParameters) {
|
|
||||||
if (has(finalParameters, key)) {
|
|
||||||
const value = finalParameters[key];
|
|
||||||
|
|
||||||
if (value !== undefined) {
|
|
||||||
payload.append(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
payload.append('Signature', signature);
|
|
||||||
|
|
||||||
return got.post<T>({
|
|
||||||
url,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
},
|
|
||||||
form: payload,
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,9 +1,6 @@
|
||||||
import { Languages } from '@logto/phrases';
|
import { Languages } from '@logto/phrases';
|
||||||
import { ArbitraryObject, Connector, ConnectorType } from '@logto/schemas';
|
import { ArbitraryObject, ConnectorType } from '@logto/schemas';
|
||||||
|
import { Response } from 'got';
|
||||||
import { Response } from './aliyun';
|
|
||||||
|
|
||||||
export * from './aliyun';
|
|
||||||
|
|
||||||
export interface ConnectorMetadata {
|
export interface ConnectorMetadata {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -15,10 +12,6 @@ export interface ConnectorMetadata {
|
||||||
configTemplate: string;
|
configTemplate: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConnectorDTO extends Connector {
|
|
||||||
metadata: ConnectorMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ConnectorErrorCodes {
|
export enum ConnectorErrorCodes {
|
||||||
General,
|
General,
|
||||||
InsufficientRequestParameters,
|
InsufficientRequestParameters,
|
||||||
|
@ -58,22 +51,22 @@ export type SendEmailResponse = { EnvId: string; RequestId: string };
|
||||||
export type SendSmsResponse = { BizId: string; Code: string; Message: string; RequestId: string };
|
export type SendSmsResponse = { BizId: string; Code: string; Message: string; RequestId: string };
|
||||||
|
|
||||||
export type EmailSendMessageFunction<
|
export type EmailSendMessageFunction<
|
||||||
T1 extends ArbitraryObject = ArbitraryObject,
|
ResponseBody extends ArbitraryObject = ArbitraryObject,
|
||||||
T2 = Response<T1>
|
ResponseType = Response<ResponseBody>
|
||||||
> = (
|
> = (
|
||||||
address: string,
|
address: string,
|
||||||
type: keyof EmailMessageTypes,
|
type: keyof EmailMessageTypes,
|
||||||
payload: EmailMessageTypes[typeof type]
|
payload: EmailMessageTypes[typeof type]
|
||||||
) => Promise<T2>;
|
) => Promise<ResponseType>;
|
||||||
|
|
||||||
export type SmsSendMessageFunction<
|
export type SmsSendMessageFunction<
|
||||||
T1 extends ArbitraryObject = ArbitraryObject,
|
ResponseBody extends ArbitraryObject = ArbitraryObject,
|
||||||
T2 = Response<T1>
|
ResponseType = Response<ResponseBody>
|
||||||
> = (
|
> = (
|
||||||
phone: string,
|
phone: string,
|
||||||
type: keyof SmsMessageTypes,
|
type: keyof SmsMessageTypes,
|
||||||
payload: SmsMessageTypes[typeof type]
|
payload: SmsMessageTypes[typeof type]
|
||||||
) => Promise<T2>;
|
) => Promise<ResponseType>;
|
||||||
|
|
||||||
export interface BaseConnector {
|
export interface BaseConnector {
|
||||||
metadata: ConnectorMetadata;
|
metadata: ConnectorMetadata;
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "./tsconfig.base",
|
"extends": "./tsconfig.base",
|
||||||
"compilerOptions": {
|
"include": ["src"]
|
||||||
"types": ["jest", "jest-matcher-specific-error"]
|
|
||||||
},
|
|
||||||
"include": ["src", "jest.config.ts"]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "./tsconfig",
|
|
||||||
"compilerOptions": {
|
|
||||||
"isolatedModules": false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { existsSync, readFileSync } from 'fs';
|
import { existsSync, readFileSync } from 'fs';
|
||||||
|
|
||||||
export const getMarkdownContents = (filePath: string, fallbackContent: string): string => {
|
export const getFileContents = (filePath: string, fallbackContent: string): string => {
|
||||||
if (existsSync(filePath)) {
|
if (existsSync(filePath)) {
|
||||||
return readFileSync(filePath, 'utf8');
|
return readFileSync(filePath, 'utf8');
|
||||||
}
|
}
|
||||||
|
|
2
pnpm-lock.yaml
generated
2
pnpm-lock.yaml
generated
|
@ -71,7 +71,6 @@ importers:
|
||||||
got: ^11.8.2
|
got: ^11.8.2
|
||||||
jest: ^27.5.1
|
jest: ^27.5.1
|
||||||
jest-matcher-specific-error: ^1.0.0
|
jest-matcher-specific-error: ^1.0.0
|
||||||
jose: ^3.14.3
|
|
||||||
lint-staged: ^11.1.1
|
lint-staged: ^11.1.1
|
||||||
prettier: ^2.3.2
|
prettier: ^2.3.2
|
||||||
ts-jest: ^27.1.1
|
ts-jest: ^27.1.1
|
||||||
|
@ -80,7 +79,6 @@ importers:
|
||||||
'@logto/phrases': link:../phrases
|
'@logto/phrases': link:../phrases
|
||||||
'@logto/schemas': link:../schemas
|
'@logto/schemas': link:../schemas
|
||||||
got: 11.8.3
|
got: 11.8.3
|
||||||
jose: 3.20.3
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@jest/types': 27.5.1
|
'@jest/types': 27.5.1
|
||||||
'@shopify/jest-koa-mocks': 3.0.8
|
'@shopify/jest-koa-mocks': 3.0.8
|
||||||
|
|
Loading…
Add table
Reference in a new issue