mirror of https://github.com/certd/certd
Merge branch 'v2-dev' into v2
# Conflicts: # packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/notification-form/index.vuepull/189/head
commit
36975b37e3
|
@ -21,10 +21,10 @@ gcloud beta publicca external-account-keys create
|
|||
|
||||
```shell
|
||||
Created an external account key
|
||||
[b64MacKey: xxxxxxxxxxxxx
|
||||
keyId: xxxxxxxxx]
|
||||
[b64MacKey: xxxxxxxxxxxxxxxx
|
||||
keyId: xxxxxxxxxxxxx]
|
||||
```
|
||||
记录以上信息备用
|
||||
记录以上信息备用(注意keyId是不带中括号的)
|
||||
|
||||
|
||||
## 3、 创建证书流水线
|
||||
|
@ -32,6 +32,6 @@ keyId: xxxxxxxxx]
|
|||
|
||||
## 4、 将key信息作为EAB授权信息
|
||||
google证书需要EAB授权, 使用第二步中的 keyId 和 b64MacKey 信息创建一条EAB授权记录
|
||||
|
||||
注意:keyId没有`]`结尾,不要把`]`也复制了
|
||||
## 5、 其他就跟正常申请证书一样了
|
||||
|
||||
|
|
|
@ -67,11 +67,11 @@ function getKeyInfo(keyPem) {
|
|||
* ```
|
||||
*/
|
||||
|
||||
async function createPrivateRsaKey(modulusLength = 2048) {
|
||||
async function createPrivateRsaKey(modulusLength = 2048, encodingType = 'pkcs8') {
|
||||
const pair = await generateKeyPair('rsa', {
|
||||
modulusLength,
|
||||
privateKeyEncoding: {
|
||||
type: 'pkcs8',
|
||||
type: encodingType,
|
||||
format: 'pem',
|
||||
},
|
||||
});
|
||||
|
@ -106,11 +106,11 @@ exports.createPrivateKey = createPrivateRsaKey;
|
|||
* ```
|
||||
*/
|
||||
|
||||
exports.createPrivateEcdsaKey = async (namedCurve = 'P-256') => {
|
||||
exports.createPrivateEcdsaKey = async (namedCurve = 'P-256', encodingType = 'pkcs8') => {
|
||||
const pair = await generateKeyPair('ec', {
|
||||
namedCurve,
|
||||
privateKeyEncoding: {
|
||||
type: 'pkcs8',
|
||||
type: encodingType,
|
||||
format: 'pem',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -32,7 +32,7 @@ exports.directory = {
|
|||
*/
|
||||
|
||||
exports.crypto = require('./crypto');
|
||||
// exports.forge = require('./crypto/forge');
|
||||
exports.forge = require('./crypto/forge');
|
||||
|
||||
/**
|
||||
* Axios
|
||||
|
|
|
@ -155,16 +155,16 @@ export interface EcdsaPublicJwk {
|
|||
}
|
||||
|
||||
export interface CryptoInterface {
|
||||
createPrivateKey(keySize?: number): Promise<PrivateKeyBuffer>;
|
||||
createPrivateRsaKey(keySize?: number): Promise<PrivateKeyBuffer>;
|
||||
createPrivateEcdsaKey(namedCurve?: 'P-256' | 'P-384' | 'P-521'): Promise<PrivateKeyBuffer>;
|
||||
createPrivateKey(keySize?: number,encodingType?:string): Promise<PrivateKeyBuffer>;
|
||||
createPrivateRsaKey(keySize?: number,encodingType?:string): Promise<PrivateKeyBuffer>;
|
||||
createPrivateEcdsaKey(namedCurve?: 'P-256' | 'P-384' | 'P-521',encodingType?:string): Promise<PrivateKeyBuffer>;
|
||||
getPublicKey(keyPem: PrivateKeyBuffer | PrivateKeyString | PublicKeyBuffer | PublicKeyString): PublicKeyBuffer;
|
||||
getJwk(keyPem: PrivateKeyBuffer | PrivateKeyString | PublicKeyBuffer | PublicKeyString): RsaPublicJwk | EcdsaPublicJwk;
|
||||
splitPemChain(chainPem: CertificateBuffer | CertificateString): string[];
|
||||
getPemBodyAsB64u(pem: CertificateBuffer | CertificateString): string;
|
||||
readCsrDomains(csrPem: CsrBuffer | CsrString): CertificateDomains;
|
||||
readCertificateInfo(certPem: CertificateBuffer | CertificateString): CertificateInfo;
|
||||
createCsr(data: CsrOptions, keyPem?: PrivateKeyBuffer | PrivateKeyString): Promise<[PrivateKeyBuffer, CsrBuffer]>;
|
||||
createCsr(data: CsrOptions, keyPem?: PrivateKeyBuffer | PrivateKeyString,encodingType?:string): Promise<[PrivateKeyBuffer, CsrBuffer]>;
|
||||
createAlpnCertificate(authz: Authorization, keyAuthorization: string, keyPem?: PrivateKeyBuffer | PrivateKeyString): Promise<[PrivateKeyBuffer, CertificateBuffer]>;
|
||||
isAlpnCertificateAuthorizationValid(certPem: CertificateBuffer | CertificateString, keyAuthorization: string): boolean;
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
17:29
|
||||
02:32
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
"@certd/plus": "1.22.1",
|
||||
"axios": "^1.7.2",
|
||||
"fix-path": "^4.0.0",
|
||||
"http-proxy-agent": "^7.0.2",
|
||||
"https-proxy-agent": "^7.0.5",
|
||||
"lodash-es": "^4.17.21",
|
||||
"node-forge": "^1.3.1",
|
||||
"nodemailer": "^6.9.3",
|
||||
|
|
|
@ -12,7 +12,7 @@ export type AccessDefine = Registrable & {
|
|||
};
|
||||
};
|
||||
export interface IAccessService {
|
||||
getById(id: any): Promise<any>;
|
||||
getById<T = any>(id: any): Promise<T>;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import _ from "lodash-es";
|
||||
import { HttpClient, ILogger } from "../utils";
|
||||
|
||||
export type PluginRequest = {
|
||||
type: "plugin" | "access";
|
||||
typeName: string;
|
||||
action: string;
|
||||
input: any;
|
||||
data: any;
|
||||
};
|
||||
|
||||
export type RequestHandleContext = {
|
||||
http: HttpClient;
|
||||
logger: ILogger;
|
||||
};
|
||||
|
||||
export class RequestHandler {
|
||||
async onRequest(req: PluginRequest, ctx: RequestHandleContext) {
|
||||
if (!req.action) {
|
||||
throw new Error("action is required");
|
||||
}
|
||||
|
||||
const methodName = `on${_.upperFirst(req.action)}`;
|
||||
|
||||
// @ts-ignore
|
||||
const method = this[methodName];
|
||||
if (method) {
|
||||
// @ts-ignore
|
||||
return await this[methodName](req.data, ctx);
|
||||
}
|
||||
throw new Error(`action ${req.action} not found`);
|
||||
}
|
||||
}
|
|
@ -4,3 +4,4 @@ export * from "./context.js";
|
|||
export * from "./storage.js";
|
||||
export * from "./file-store.js";
|
||||
export * from "./license.js";
|
||||
export * from "./handler.js";
|
||||
|
|
|
@ -46,6 +46,7 @@ export type Stage = Runnable & {
|
|||
tasks: Task[];
|
||||
concurrency: ConcurrencyStrategy;
|
||||
next: NextStrategy;
|
||||
maxTaskCount?: number;
|
||||
};
|
||||
|
||||
export type Trigger = {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import axios, { AxiosRequestConfig } from "axios";
|
||||
import { logger } from "./util.log.js";
|
||||
import { Logger } from "log4js";
|
||||
import { ProxyAgent, ProxyAgentOptions } from "proxy-agent";
|
||||
import { HttpProxyAgent } from "http-proxy-agent";
|
||||
import { HttpsProxyAgent } from "https-proxy-agent";
|
||||
import nodeHttp from "http";
|
||||
export class HttpError extends Error {
|
||||
status?: number;
|
||||
statusText?: string;
|
||||
|
@ -55,9 +57,10 @@ export function createAxiosService({ logger }: { logger: Logger }) {
|
|||
}
|
||||
let agents = defaultAgents;
|
||||
if (config.skipSslVerify) {
|
||||
agents = createAgent({ rejectUnauthorized: config.rejectUnauthorized });
|
||||
logger.info("跳过SSL验证");
|
||||
agents = createAgent({ rejectUnauthorized: false } as any);
|
||||
}
|
||||
|
||||
delete config.skipSslVerify;
|
||||
config.httpsAgent = agents.httpsAgent;
|
||||
config.httpAgent = agents.httpAgent;
|
||||
|
||||
|
@ -118,7 +121,9 @@ export function createAxiosService({ logger }: { logger: Logger }) {
|
|||
`请求出错:status:${error.response?.status},statusText:${error.response?.statusText},url:${error.config?.url},method:${error.config?.method}。`
|
||||
);
|
||||
logger.error("返回数据:", JSON.stringify(error.response?.data));
|
||||
|
||||
if (error.response?.data) {
|
||||
error.message = error.response.data.message || error.response.data.msg || error.response.data.error || error.response.data;
|
||||
}
|
||||
if (error instanceof AggregateError) {
|
||||
logger.error("AggregateError", error);
|
||||
}
|
||||
|
@ -138,10 +143,19 @@ export type HttpClient = {
|
|||
request<D = any, R = any>(config: HttpRequestConfig<D>): Promise<HttpClientResponse<R>>;
|
||||
};
|
||||
|
||||
export function createAgent(opts: ProxyAgentOptions = {}) {
|
||||
const httpAgent = new ProxyAgent(opts);
|
||||
export function createAgent(opts: nodeHttp.AgentOptions = {}) {
|
||||
let httpAgent, httpsAgent;
|
||||
const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy;
|
||||
if (httpProxy) {
|
||||
httpAgent = new HttpProxyAgent(httpProxy, opts as any);
|
||||
}
|
||||
const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
|
||||
if (httpsProxy) {
|
||||
httpsAgent = new HttpsProxyAgent(httpsProxy, opts as any);
|
||||
}
|
||||
|
||||
return {
|
||||
httpAgent,
|
||||
httpsAgent: httpAgent,
|
||||
httpsAgent,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"nanoid": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^23.0.4",
|
||||
|
|
|
@ -1,31 +1,63 @@
|
|||
import { nanoid } from 'nanoid';
|
||||
|
||||
export type IframeMessageData = {
|
||||
export type IframeMessageData<T> = {
|
||||
action: string;
|
||||
id: string;
|
||||
data: any;
|
||||
data?: T;
|
||||
replyId?: string;
|
||||
errorCode?: number; //0为成功
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export type IframeMessageReq = {
|
||||
req: IframeMessageData;
|
||||
onReply: (data: IframeMessageData) => void;
|
||||
export type IframeMessageReq<T = any, R = any> = {
|
||||
req: IframeMessageData<T>;
|
||||
onReply: (data: IframeMessageData<R>) => void;
|
||||
};
|
||||
|
||||
export class IframeException extends Error {
|
||||
code?: number = 0;
|
||||
constructor(data: IframeMessageData<any>) {
|
||||
super(data.message);
|
||||
this.code = data.errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
export class IframeClient {
|
||||
messageBucket: Record<string, IframeMessageReq> = {};
|
||||
requestQueue: Record<string, IframeMessageReq> = {};
|
||||
//当前客户端是否是父级页面
|
||||
iframe?: HTMLIFrameElement;
|
||||
constructor(iframe?: HTMLIFrameElement) {
|
||||
onError?: any;
|
||||
|
||||
handlers: Record<string, (data: IframeMessageData<any>) => Promise<void>> = {};
|
||||
constructor(iframe?: HTMLIFrameElement, onError?: (e: any) => void) {
|
||||
this.iframe = iframe;
|
||||
window.addEventListener('message', (event: MessageEvent<IframeMessageData>) => {
|
||||
this.onError = onError;
|
||||
window.addEventListener('message', async (event: MessageEvent<IframeMessageData<any>>) => {
|
||||
const data = event.data;
|
||||
if (data.replyId) {
|
||||
const req = this.messageBucket[data.replyId];
|
||||
if (data.action) {
|
||||
console.log(`收到消息[isSub:${this.isInFrame()}]`, data);
|
||||
try {
|
||||
const handler = this.handlers[data.action];
|
||||
if (handler) {
|
||||
const res = await handler(data);
|
||||
if (data.id && data.action !== 'reply') {
|
||||
await this.send('reply', res, data.id);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`action:${data.action} 未注册处理器,可能版本过低`);
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
await this.send('reply', {}, data.id, 500, e.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.register('reply', async data => {
|
||||
const req = this.requestQueue[data.replyId!];
|
||||
if (req) {
|
||||
req.onReply(data);
|
||||
delete this.messageBucket[data.replyId];
|
||||
}
|
||||
delete this.requestQueue[data.replyId!];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -33,23 +65,45 @@ export class IframeClient {
|
|||
return window.self !== window.top;
|
||||
}
|
||||
|
||||
async send(action: string, data?: any, replyId?: string) {
|
||||
const reqMessageData: IframeMessageData = {
|
||||
register<T = any>(action: string, handler: (data: IframeMessageData<T>) => Promise<void>) {
|
||||
this.handlers[action] = handler;
|
||||
}
|
||||
|
||||
async send<R = any, T = any>(action: string, data?: T, replyId?: string, errorCode?: number, message?: string): Promise<IframeMessageData<R>> {
|
||||
try {
|
||||
return await this.doSend<R, T>(action, data, replyId, errorCode, message);
|
||||
} catch (e) {
|
||||
if (this.onError) {
|
||||
this.onError(e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async doSend<R = any, T = any>(action: string, data?: T, replyId?: string, errorCode?: number, message?: string): Promise<IframeMessageData<R>> {
|
||||
const reqMessageData: IframeMessageData<T> = {
|
||||
id: nanoid(),
|
||||
action,
|
||||
data,
|
||||
replyId,
|
||||
errorCode,
|
||||
message,
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const onReply = async (reply: IframeMessageData) => {
|
||||
const onReply = (reply: IframeMessageData<R>) => {
|
||||
if (reply.errorCode && reply.errorCode > 0) {
|
||||
reject(new IframeException(reply));
|
||||
return;
|
||||
}
|
||||
resolve(reply);
|
||||
};
|
||||
this.messageBucket[reqMessageData.id] = {
|
||||
this.requestQueue[reqMessageData.id] = {
|
||||
req: reqMessageData,
|
||||
onReply,
|
||||
};
|
||||
try {
|
||||
console.log(`send message[isSub:${this.isInFrame()}]:`, reqMessageData);
|
||||
if (!this.iframe) {
|
||||
if (!window.parent) {
|
||||
reject('当前页面不在 iframe 中');
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"env": {
|
||||
"mocha": true
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/ban-ts-ignore": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
// "no-unused-expressions": "off",
|
||||
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
test/user.secret.ts
|
||||
|
||||
.rollup.cache
|
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
src
|
||||
.rollup.cache
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"printWidth": 160
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.24.3](https://github.com/certd/certd/compare/v1.24.2...v1.24.3) (2024-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.24.2](https://github.com/certd/certd/compare/v1.24.1...v1.24.2) (2024-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.24.1](https://github.com/certd/certd/compare/v1.24.0...v1.24.1) (2024-09-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.22.1](https://github.com/certd/certd/compare/v1.22.0...v1.22.1) (2024-07-20)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
# [1.22.0](https://github.com/certd/certd/compare/v1.21.2...v1.22.0) (2024-07-19)
|
||||
|
||||
### Features
|
||||
|
||||
* 升级midway,支持esm ([485e603](https://github.com/certd/certd/commit/485e603b5165c28bc08694997726eaf2a585ebe7))
|
||||
* 支持postgresql ([3b19bfb](https://github.com/certd/certd/commit/3b19bfb4291e89064b3b407a80dae092d54747d5))
|
|
@ -0,0 +1,16 @@
|
|||
# Vue 3 + TypeScript + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
|
||||
|
||||
## Type Support For `.vue` Imports in TS
|
||||
|
||||
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's Take Over mode by following these steps:
|
||||
|
||||
1. Run `Extensions: Show Built-in Extensions` from VS Code's command palette, look for `TypeScript and JavaScript Language Features`, then right click and select `Disable (Workspace)`. By default, Take Over mode will enable itself if the default TypeScript extension is disabled.
|
||||
2. Reload the VS Code window by running `Developer: Reload Window` from the command palette.
|
||||
|
||||
You can learn more about Take Over mode [here](https://github.com/johnsoncodehk/volar/discussions/471).
|
|
@ -0,0 +1,96 @@
|
|||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
// https://gist.github.com/lovasoa/8691344
|
||||
async function* walk(dir) {
|
||||
for await (const d of await fs.promises.opendir(dir)) {
|
||||
const entry = path.join(dir, d.name);
|
||||
if (d.isDirectory()) {
|
||||
yield* walk(entry);
|
||||
} else if (d.isFile()) {
|
||||
yield entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolveImportPath(sourceFile, importPath, options) {
|
||||
const sourceFileAbs = path.resolve(process.cwd(), sourceFile);
|
||||
const root = path.dirname(sourceFileAbs);
|
||||
const { moduleFilter = defaultModuleFilter } = options;
|
||||
|
||||
if (moduleFilter(importPath)) {
|
||||
const importPathAbs = path.resolve(root, importPath);
|
||||
let possiblePath = [path.resolve(importPathAbs, "./index.ts"), path.resolve(importPathAbs, "./index.js"), importPathAbs + ".ts", importPathAbs + ".js"];
|
||||
|
||||
if (possiblePath.length) {
|
||||
for (let i = 0; i < possiblePath.length; i++) {
|
||||
let entry = possiblePath[i];
|
||||
if (fs.existsSync(entry)) {
|
||||
const resolved = path.relative(root, entry.replace(/\.ts$/, ".js"));
|
||||
|
||||
if (!resolved.startsWith(".")) {
|
||||
return "./" + resolved;
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function replace(filePath, outFilePath, options) {
|
||||
const code = fs.readFileSync(filePath).toString();
|
||||
const newCode = code.replace(/(import|export) (.+?) from ('[^\n']+'|"[^\n"]+");/gs, function (found, action, imported, from) {
|
||||
const importPath = from.slice(1, -1);
|
||||
let resolvedPath = resolveImportPath(filePath, importPath, options);
|
||||
|
||||
if (resolvedPath) {
|
||||
resolvedPath = resolvedPath.replaceAll("\\", "/");
|
||||
console.log("\t", importPath, resolvedPath);
|
||||
return `${action} ${imported} from "${resolvedPath}";`;
|
||||
}
|
||||
|
||||
return found;
|
||||
});
|
||||
|
||||
if (code !== newCode) {
|
||||
fs.writeFileSync(outFilePath, newCode);
|
||||
}
|
||||
}
|
||||
|
||||
// Then, use it with a simple async for loop
|
||||
async function run(srcDir, options = defaultOptions) {
|
||||
const { sourceFileFilter = defaultSourceFileFilter } = options;
|
||||
|
||||
for await (const entry of walk(srcDir)) {
|
||||
if (sourceFileFilter(entry)) {
|
||||
console.log(entry);
|
||||
replace(entry, entry, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const defaultSourceFileFilter = function (sourceFilePath) {
|
||||
return /\.(js|ts)$/.test(sourceFilePath) && !/node_modules/.test(sourceFilePath);
|
||||
};
|
||||
|
||||
const defaultModuleFilter = function (importedModule) {
|
||||
return !path.isAbsolute(importedModule) && !importedModule.startsWith("@") && !importedModule.endsWith(".js");
|
||||
};
|
||||
|
||||
const defaultOptions = {
|
||||
sourceFileFilter: defaultSourceFileFilter,
|
||||
moduleFilter: defaultModuleFilter,
|
||||
};
|
||||
|
||||
// Switch this to test on one file or directly run on a directory.
|
||||
const DEBUG = false;
|
||||
|
||||
if (DEBUG) {
|
||||
replace("./src/index.ts", "./out.ts", defaultOptions);
|
||||
} else {
|
||||
await run("./src/", defaultOptions);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "@certd/lib-jdcloud",
|
||||
"private": false,
|
||||
"version": "1.24.4",
|
||||
"main": "./dist/bundle.mjs",
|
||||
"module": "./dist/bundle.mjs",
|
||||
"types": "./dist/d/index.d.ts",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "rollup -c ",
|
||||
"build2": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-register": "^6.26.0",
|
||||
"buffer": "^5.0.8",
|
||||
"create-hash": "^1.1.3",
|
||||
"create-hmac": "^1.1.6",
|
||||
"debug": "^3.1.0",
|
||||
"node-fetch": "^2.1.2",
|
||||
"url": "^0.11.0",
|
||||
"uuid": "^3.1.0",
|
||||
"@certd/pipeline": "1.21.0",
|
||||
"axios": "^1.7.2",
|
||||
"rollup": "^3.7.4"
|
||||
},
|
||||
"gitHead": "c49ccbde93dbad7062ac39d4f18eca7d561f573f"
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
const resolve = require("@rollup/plugin-node-resolve");
|
||||
const commonjs = require("@rollup/plugin-commonjs");
|
||||
//const Typescript = require("rollup-plugin-typescript2");
|
||||
const Typescript = require("@rollup/plugin-typescript");
|
||||
const json = require("@rollup/plugin-json");
|
||||
const terser = require("@rollup/plugin-terser");
|
||||
module.exports = {
|
||||
input: "src/index.js",
|
||||
output: {
|
||||
file: "dist/bundle.mjs",
|
||||
format: "es",
|
||||
},
|
||||
plugins: [
|
||||
// 解析第三方依赖
|
||||
resolve({
|
||||
jsnext: true,
|
||||
main: true,
|
||||
browser: true,
|
||||
}),
|
||||
// 识别 commonjs 模式第三方依赖
|
||||
commonjs({
|
||||
// dynamicRequireRoot: "../../../../",
|
||||
// dynamicRequireTargets: [
|
||||
// // include using a glob pattern (either a string or an array of strings)
|
||||
// "../../../../**/shelljs/src/*",
|
||||
// ],
|
||||
}),
|
||||
// Typescript({
|
||||
// target: "esnext",
|
||||
// rootDir: "src",
|
||||
// declaration: true,
|
||||
// declarationDir: "dist/d",
|
||||
// exclude: ["./node_modules/**", "./src/**/*.vue", "./src/**/*.spec.ts"],
|
||||
// allowSyntheticDefaultImports: true,
|
||||
// }),
|
||||
json(),
|
||||
terser(),
|
||||
],
|
||||
external: ["vue", "lodash-es", "dayjs", "log4js", "@midwayjs/core", "@certd/pipeline", "axios", "querystring"],
|
||||
};
|
|
@ -0,0 +1,2 @@
|
|||
require('./lib/node_loader')
|
||||
module.exports = require('./lib/core')
|
|
@ -0,0 +1 @@
|
|||
module.exports.DomainService = require("./repo/domainservice/v2/domainservice.js");
|
|
@ -0,0 +1 @@
|
|||
module.exports = require('./lib/jc')
|
|
@ -0,0 +1,4 @@
|
|||
require("babel-polyfill");
|
||||
require("./browser_loader");
|
||||
var JC = require("./core");
|
||||
module.exports = JC;
|
|
@ -0,0 +1,13 @@
|
|||
var util = require('./util')
|
||||
util.crypto.lib = {
|
||||
createHash: require('create-hash'),
|
||||
createHmac: require('create-hmac')
|
||||
}
|
||||
util.Buffer = require('buffer/').Buffer
|
||||
util.url = require('url/')
|
||||
util.querystring = require('querystring/')
|
||||
util.environment = 'js'
|
||||
|
||||
var JC = require('./core')
|
||||
|
||||
module.exports = JC
|
|
@ -0,0 +1,6 @@
|
|||
require('./core')
|
||||
|
||||
require('./config')
|
||||
require('./request')
|
||||
require('./service')
|
||||
require('./credentials')
|
|
@ -0,0 +1,78 @@
|
|||
var JDCloud = require('./core')
|
||||
|
||||
let defaultValues = {
|
||||
credentials: null,
|
||||
regionId: null,
|
||||
apiVersions: null,
|
||||
endpoint: {},
|
||||
version: {},
|
||||
logger: function (string, level = 'INFO') {
|
||||
// level: INFO / DEBUG / ERROR / WARN
|
||||
console.log(string)
|
||||
}
|
||||
}
|
||||
JDCloud.Config = class Config {
|
||||
constructor (options = {}) {
|
||||
options = this.extractCredentials(options)
|
||||
|
||||
JDCloud.util.each.call(this, defaultValues, function (key, value) {
|
||||
if (options[key] === undefined) {
|
||||
this[key] = value
|
||||
} else {
|
||||
this[key] = options[key]
|
||||
}
|
||||
})
|
||||
JDCloud.util.each.call(this, JDCloud.Service._services, function (
|
||||
key,
|
||||
value
|
||||
) {
|
||||
if (options[key] !== undefined) {
|
||||
this[key] = options[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
extractCredentials (options) {
|
||||
if (options.accessKeyId && options.secretAccessKey) {
|
||||
options = Object.assign({}, options)
|
||||
options.credentials = new JDCloud.Credentials(options)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
getCredentials () {
|
||||
var p = new Promise((resolve, reject) => {
|
||||
if (this.credentials) {
|
||||
if (typeof this.credentials.get === 'function') {
|
||||
} else if (
|
||||
this.credentials.accessKeyId &&
|
||||
this.credentials.secretAccessKey
|
||||
) {
|
||||
resolve()
|
||||
} else {
|
||||
reject(new Error('missing credentials'))
|
||||
}
|
||||
} else if (this.credentialProvider) {
|
||||
} else {
|
||||
reject(new Error('get credentials failed'))
|
||||
}
|
||||
})
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
update (options, allowUnknownKeys = false) {
|
||||
options = this.extractCredentials(options)
|
||||
JDCloud.util.each.call(this, options, function (key, value) {
|
||||
if (
|
||||
allowUnknownKeys ||
|
||||
defaultValues.hasOwnProperty(key) ||
|
||||
JDCloud.Service.hasService(key)
|
||||
) {
|
||||
this[key] = options[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
JDCloud.config = new JDCloud.Config()
|
|
@ -0,0 +1,12 @@
|
|||
var JDCloud = {
|
||||
util: require("./util"),
|
||||
// todo swaggerVar
|
||||
VERSION: "",
|
||||
fetch: require("node-fetch"),
|
||||
};
|
||||
|
||||
module.exports = JDCloud;
|
||||
|
||||
require("./service");
|
||||
require("./config");
|
||||
require("./request");
|
|
@ -0,0 +1,21 @@
|
|||
var JDCloud = require('./core')
|
||||
|
||||
JDCloud.Credentials = class Credentials {
|
||||
constructor () {
|
||||
this.expired = false
|
||||
this.expireTime = null
|
||||
|
||||
if (arguments.length === 1 && typeof arguments[0] === 'object') {
|
||||
var creds = arguments[0].credentials || arguments[0]
|
||||
this.accessKeyId = creds.accessKeyId
|
||||
this.secretAccessKey = creds.secretAccessKey
|
||||
this.sessionToken = creds.sessionToken
|
||||
} else {
|
||||
this.accessKeyId = arguments[0]
|
||||
this.secretAccessKey = arguments[1]
|
||||
this.sessionToken = arguments[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = JDCloud.Credentials
|
|
@ -0,0 +1,3 @@
|
|||
require('./node_loader')
|
||||
var JDCloud = require('./core')
|
||||
module.exports = JDCloud
|
|
@ -0,0 +1,12 @@
|
|||
var util = require('./util')
|
||||
|
||||
util.crypto.lib = require('crypto')
|
||||
util.Buffer = require('buffer').Buffer
|
||||
util.url = require('url')
|
||||
util.querystring = require('querystring')
|
||||
util.environment = 'nodejs'
|
||||
let JDCloud = require('./core')
|
||||
JDCloud.fetch = require('node-fetch')
|
||||
module.exports = JDCloud
|
||||
|
||||
require('./credentials')
|
|
@ -0,0 +1,137 @@
|
|||
var JDCloud = require("./core");
|
||||
|
||||
let util = JDCloud.util;
|
||||
JDCloud.JCRequest = class JCRequest {
|
||||
constructor(service, path, httpMethod, pathParams, queryParams, headerParams, formParams, postBody, contentTypes, accepts, returnType) {
|
||||
this.service = service;
|
||||
|
||||
var endpoint = service.config.endpoint;
|
||||
pathParams.regionId = pathParams.regionId || service.config.regionId;
|
||||
this.regionId = pathParams.regionId;
|
||||
|
||||
this.path = this.buildPath(path, pathParams);
|
||||
this.path = util.uriEscapePath(this.path);
|
||||
|
||||
var queryString = this.buildQuery(queryParams);
|
||||
|
||||
var url = this.path;
|
||||
if (queryString) {
|
||||
url = this.path + "?" + queryString;
|
||||
}
|
||||
|
||||
var contentType = this.jsonPreferredMime(contentTypes) || "application/json";
|
||||
headerParams["content-type"] = contentType;
|
||||
var requestHeaders = this.buildHeaders(headerParams);
|
||||
|
||||
var requestInit = {
|
||||
method: httpMethod || "GET",
|
||||
headers: requestHeaders,
|
||||
};
|
||||
|
||||
if (contentType === "application/x-www-form-urlencoded") {
|
||||
} else if (contentType === "multipart/form-data") {
|
||||
} else if (postBody) {
|
||||
requestInit.body = JSON.stringify(postBody);
|
||||
}
|
||||
var fetchUrl = endpoint.protocol + "://" + endpoint.host + url;
|
||||
JDCloud.config.logger(`make request where url is :${fetchUrl} \nwith fetch config:${JSON.stringify(requestInit)}`);
|
||||
this.request = new JDCloud.fetch.Request(fetchUrl, requestInit);
|
||||
this.request.bodyCache = requestInit.body;
|
||||
}
|
||||
|
||||
buildPath(path, pathParams) {
|
||||
var uri = (this.service.config.basePath || "") + path;
|
||||
uri = uri.replace(/\{([\w-]+)\}/g, (fullMatch, key) => {
|
||||
var value;
|
||||
if (pathParams.hasOwnProperty(key)) {
|
||||
value = pathParams[key];
|
||||
} else {
|
||||
value = fullMatch;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
return uri;
|
||||
}
|
||||
|
||||
buildQuery(queryParams) {
|
||||
var queryParamsWithoutEmptyItem = {};
|
||||
var keys = Object.keys(queryParams);
|
||||
for (let key of keys) {
|
||||
if (queryParams[key] !== undefined) {
|
||||
queryParamsWithoutEmptyItem[key] = queryParams[key];
|
||||
}
|
||||
}
|
||||
return JDCloud.util.querystring.stringify(queryParamsWithoutEmptyItem);
|
||||
}
|
||||
|
||||
search() {
|
||||
var query = this.request.url.split("?", 2)[1];
|
||||
if (query) {
|
||||
query = JDCloud.util.querystring.parse(query);
|
||||
return JDCloud.util.queryParamsToString(query);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
digitizationArray(key, obj) {
|
||||
var result = key;
|
||||
if (Array.isArray(obj)) {
|
||||
JDCloud.util.arrayEach(obj, (arrayValue, index) => {
|
||||
result += this.digitizationArray(`.${index + 1}`, arrayValue);
|
||||
});
|
||||
} else if (typeof obj === "object" && obj != null) {
|
||||
JDCloud.util.each(obj, (key, ObjValue) => {
|
||||
result += `.name=${key}&${result}.values` + this.digitizationArray();
|
||||
result += key + ".name=" + ObjValue + "&" + this.digitizationArray(key + ".values", ObjValue);
|
||||
});
|
||||
} else {
|
||||
result += key + "=" + encodeURI(obj);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
buildHeaders(headerParams) {
|
||||
var headers = new JDCloud.fetch.Headers({
|
||||
accept: "application/json",
|
||||
});
|
||||
|
||||
util.each.call(this, headerParams, function (key) {
|
||||
if (headerParams[key] !== undefined && headerParams[key] != null) {
|
||||
headers.append(key, headerParams[key]);
|
||||
}
|
||||
});
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given content type represents JSON.<br>
|
||||
* JSON content type examples:<br>
|
||||
* <ul>
|
||||
* <li>application/json</li>
|
||||
* <li>application/json; charset=UTF8</li>
|
||||
* <li>APPLICATION/JSON</li>
|
||||
* </ul>
|
||||
* @param {String} contentType The MIME content type to check.
|
||||
* @returns {Boolean} <code>true</code> if <code>contentType</code> represents JSON, otherwise <code>false</code>.
|
||||
*/
|
||||
isJsonMime(contentType) {
|
||||
return Boolean(contentType != null && contentType.match(/^application\/json(;.*)?$/i));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses a content type from the given array, with JSON preferred; i.e. return JSON if included, otherwise return the first.
|
||||
* @param {Array.<String>} contentTypes
|
||||
* @returns {String} The chosen content type, preferring JSON.
|
||||
*/
|
||||
jsonPreferredMime(contentTypes) {
|
||||
for (var i = 0; i < contentTypes.length; i++) {
|
||||
if (this.isJsonMime(contentTypes[i])) {
|
||||
return contentTypes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return contentTypes[0];
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = JDCloud.JCRequest;
|
|
@ -0,0 +1,231 @@
|
|||
var JDCloud = require('./core')
|
||||
var SignerV2 = require('./signers/v2')
|
||||
|
||||
JDCloud.Service = class Service {
|
||||
constructor (serviceId, config = {}) {
|
||||
this.serviceId = serviceId
|
||||
this.init(config)
|
||||
}
|
||||
|
||||
init (config) {
|
||||
// 某个服务类型的全局配置
|
||||
var serviceConfig = JDCloud.config[this.serviceId]
|
||||
// 全局配置
|
||||
this.config = new JDCloud.Config(JDCloud.config)
|
||||
if (serviceConfig) {
|
||||
this.config.update(serviceConfig, true)
|
||||
}
|
||||
if (config) {
|
||||
if (!this.config.endpoint.host && !config.endpoint) {
|
||||
config.endpoint = config._defaultEndpoint
|
||||
}
|
||||
delete config._defaultEndpoint
|
||||
this.config.update(config, true)
|
||||
}
|
||||
}
|
||||
|
||||
makeRequest (
|
||||
path,
|
||||
httpMethod,
|
||||
pathParams,
|
||||
queryParams,
|
||||
headerParams,
|
||||
formParams,
|
||||
postBody,
|
||||
contentTypes,
|
||||
accepts,
|
||||
returnType,
|
||||
callback
|
||||
) {
|
||||
var request = new JDCloud.JCRequest(
|
||||
this,
|
||||
path,
|
||||
httpMethod,
|
||||
pathParams,
|
||||
queryParams,
|
||||
headerParams,
|
||||
formParams,
|
||||
postBody,
|
||||
contentTypes,
|
||||
accepts,
|
||||
returnType
|
||||
)
|
||||
|
||||
var signer = new SignerV2(request, this.serviceId)
|
||||
|
||||
return this.config.getCredentials().then(() => {
|
||||
signer.addAuthorization(this.config.credentials)
|
||||
return JDCloud.fetch(signer.request.request).then(response => {
|
||||
return response.json().then(
|
||||
result => {
|
||||
result.responseObj = response
|
||||
if (response.ok) {
|
||||
return result
|
||||
}
|
||||
return Promise.reject(result)
|
||||
},
|
||||
error => {
|
||||
error.responseObj = response
|
||||
if (error.type === 'invalid-json') {
|
||||
// oss没有返回json
|
||||
if (response.ok) {
|
||||
return Promise.resolve({
|
||||
requestId: response.headers.get('x-jdcloud-request-id') || ''
|
||||
})
|
||||
} else {
|
||||
/* eslint-disable */
|
||||
return Promise.reject({
|
||||
requestId: response.headers.get('x-jdcloud-request-id') || ''
|
||||
})
|
||||
/* eslint-enable */
|
||||
}
|
||||
}
|
||||
throw error
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
buildCollectionParam (param, collectionFormat) {
|
||||
if (param === null || param === undefined) {
|
||||
return param
|
||||
}
|
||||
switch (collectionFormat) {
|
||||
case 'csv':
|
||||
return param.map(this.paramToString).join(',')
|
||||
case 'ssv':
|
||||
return param.map(this.paramToString).join(' ')
|
||||
case 'tsv':
|
||||
return param.map(this.paramToString).join('\t')
|
||||
case 'pipes':
|
||||
return param.map(this.paramToString).join('|')
|
||||
case 'multi':
|
||||
// return the array directly as SuperAgent will handle it as expected
|
||||
return param.map(this.paramToString)
|
||||
default:
|
||||
throw new Error('Unknown collection format: ' + collectionFormat)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* filter is a special type of array
|
||||
* only contains:
|
||||
* [
|
||||
* { name: someString , values:[ someString, someString ] ,operator: someString}
|
||||
* ]
|
||||
*
|
||||
*/
|
||||
buildFilterParam (param, key) {
|
||||
var result = {}
|
||||
if (Array.isArray(param)) {
|
||||
let index = 0
|
||||
for (var i = 0; i < param.length; i++) {
|
||||
var obj = param[i]
|
||||
|
||||
// 兼容空字符串
|
||||
if (obj.values !== '' && !Array.isArray(obj.values)) {
|
||||
throw new Error('The type of filters.values should be Array!')
|
||||
}
|
||||
if (obj.name && obj.values) {
|
||||
if (!obj.values.length) continue
|
||||
result[`${key}.${index + 1}.name`] = obj.name
|
||||
for (var j = 0; j < obj.values.length; j++) {
|
||||
var someString = obj.values[j]
|
||||
result[`${key}.${index + 1}.values.${j + 1}`] = someString
|
||||
}
|
||||
if (obj.operator) {
|
||||
result[`${key}.${index + 1}.operator`] = obj.operator
|
||||
}
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
buildTagFilterParam (param = [], key) {
|
||||
var result = {}
|
||||
if (!Array.isArray(param)) {
|
||||
throw new Error(`The type of param 'param' should be Array!`)
|
||||
}
|
||||
|
||||
for (var i = 0; i < param.length; i++) {
|
||||
var obj = param[i]
|
||||
|
||||
if (obj.values && !Array.isArray(obj.values)) {
|
||||
throw new Error(
|
||||
`The type of param 'param[${i}].values' should be Array or NULL!`
|
||||
)
|
||||
}
|
||||
|
||||
if (obj.key) {
|
||||
result[`${key}.${i + 1}.key`] = obj.key
|
||||
|
||||
if (obj.values) {
|
||||
for (var j = 0; j < obj.values.length; j++) {
|
||||
var someString = obj.values[j]
|
||||
result[`${key}.${i + 1}.values.${j + 1}`] = someString
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
buildSortParam (param = [], key) {
|
||||
var result = {}
|
||||
if (!Array.isArray(param)) {
|
||||
throw new Error(`The type of param 'param' should be Array!`)
|
||||
}
|
||||
|
||||
var index = 0
|
||||
for (var i = 0; i < param.length; i++) {
|
||||
var obj = param[i]
|
||||
|
||||
if (obj.name && obj.direction) {
|
||||
index++
|
||||
result[`${key}.${index}.name`] = obj.name
|
||||
result[`${key}.${index}.direction`] = obj.direction
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// arr=[a,b,c] => arr={arr1:a, arr2:b, arr3:c}
|
||||
buildArrayParam (param = [], key) {
|
||||
var result = {}
|
||||
if (!Array.isArray(param)) {
|
||||
throw new Error(`The type of param 'param' should be Array!`)
|
||||
}
|
||||
|
||||
for (var i = 0; i < param.length; i++) {
|
||||
var value = param[i]
|
||||
result[`${key}.${i + 1}`] = value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation for an actual parameter.
|
||||
* @param param The actual parameter.
|
||||
* @returns {String} The string representation of <code>param</code>.
|
||||
*/
|
||||
paramToString (param) {
|
||||
if (param === undefined || param === null) {
|
||||
return ''
|
||||
}
|
||||
if (param instanceof Date) {
|
||||
return param.toJSON()
|
||||
}
|
||||
|
||||
return param.toString()
|
||||
}
|
||||
|
||||
static hasService (id) {
|
||||
return JDCloud.Service._services.hasOwnProperty(id)
|
||||
}
|
||||
}
|
||||
JDCloud.Service._services = {}
|
||||
|
||||
module.exports = JDCloud.Service
|
|
@ -0,0 +1,13 @@
|
|||
module.exports = class RequestSigner {
|
||||
constructor (request) {
|
||||
this.request = request
|
||||
}
|
||||
|
||||
setServiceClientId (id) {
|
||||
this.serviceClientId = id
|
||||
}
|
||||
|
||||
getServiceClientId () {
|
||||
return this.serviceClientId
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
// Copyright 2018 JDCLOUD.COM
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License
|
||||
// This signer is modified from AWS V4 signer algorithm.
|
||||
|
||||
var util = require("../util");
|
||||
var RequestSigner = require("./request_signer");
|
||||
var v2Credentials = require("./v2_credentials");
|
||||
var uuid = require("uuid");
|
||||
var JDCloud = require("../core");
|
||||
|
||||
module.exports = class SignerV2 extends RequestSigner {
|
||||
constructor(request, serviceName, options = {}) {
|
||||
super(request);
|
||||
this.signatureCache = true;
|
||||
this.algorithm = "JDCLOUD2-HMAC-SHA256";
|
||||
this.unsignableHeaders = ["authorization", "user-agent"];
|
||||
this.serviceName = serviceName;
|
||||
// this.signatureCache = typeof options.signatureCache === 'boolean' ? options.signatureCache : true;
|
||||
}
|
||||
|
||||
// 签名流程见 https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
|
||||
|
||||
addAuthorization(credentials, date) {
|
||||
// var datetime = '20180119T070300Z';
|
||||
var datetime = util.date.iso8601(date).replace(/[:-]|\.\d{3}/g, "");
|
||||
this.addHeaders(credentials, datetime);
|
||||
this.request.request.headers.set("Authorization", this.authorization(credentials, datetime));
|
||||
}
|
||||
|
||||
addHeaders(credentials, datetime) {
|
||||
this.request.request.headers.set("x-jdcloud-date", datetime);
|
||||
this.request.request.headers.set("x-jdcloud-nonce", uuid.v4());
|
||||
this.request.request.headers.set("host", this.request.service.config.endpoint.host);
|
||||
}
|
||||
|
||||
signedHeaders() {
|
||||
var keys = [];
|
||||
this.request.request.headers.forEach((value, key) => {
|
||||
key = key.toLowerCase();
|
||||
if (this.isSignableHeader(key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
});
|
||||
/* util.each.call(this, this.request.headers, function (key) {
|
||||
|
||||
}); */
|
||||
return keys.sort().join(";");
|
||||
}
|
||||
|
||||
credentialString(datetime) {
|
||||
return v2Credentials.createScope(datetime.substr(0, 8), this.request.regionId, this.serviceName);
|
||||
}
|
||||
|
||||
signature(credentials, datetime) {
|
||||
var signingKey = v2Credentials.getSigningKey(credentials, datetime.substr(0, 8), this.request.regionId, this.serviceName, this.signatureCache);
|
||||
return util.crypto.hmac(signingKey, this.stringToSign(datetime), "hex");
|
||||
}
|
||||
|
||||
stringToSign(datetime) {
|
||||
var parts = [];
|
||||
parts.push(this.algorithm);
|
||||
parts.push(datetime);
|
||||
parts.push(this.credentialString(datetime));
|
||||
parts.push(this.hexEncodedHash(this.canonicalString()));
|
||||
JDCloud.config.logger("StringToSign is \n" + JSON.stringify(parts), "DEBUG");
|
||||
return parts.join("\n");
|
||||
}
|
||||
|
||||
// 构建标准签名字符串
|
||||
canonicalString() {
|
||||
var parts = [];
|
||||
var pathname = this.request.path;
|
||||
// if (this.serviceName !== 'jfs') {
|
||||
// pathname = util.uriEscapePath(pathname)
|
||||
// }
|
||||
|
||||
parts.push(this.request.request.method);
|
||||
parts.push(pathname);
|
||||
parts.push(this.request.search());
|
||||
parts.push(this.canonicalHeaders() + "\n");
|
||||
parts.push(this.signedHeaders());
|
||||
parts.push(this.hexEncodedBodyHash());
|
||||
JDCloud.config.logger("canonicalString is \n" + JSON.stringify(parts), "DEBUG");
|
||||
return parts.join("\n");
|
||||
}
|
||||
|
||||
canonicalHeaders() {
|
||||
var headers = [];
|
||||
this.request.request.headers.forEach((value, key) => {
|
||||
headers.push([key, value]);
|
||||
});
|
||||
headers.sort(function (a, b) {
|
||||
return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : 1;
|
||||
});
|
||||
var parts = [];
|
||||
util.arrayEach.call(this, headers, function (item) {
|
||||
var key = item[0].toLowerCase();
|
||||
if (this.isSignableHeader(key)) {
|
||||
var value = item[1];
|
||||
if (typeof value === "undefined" || value === null || typeof value.toString !== "function") {
|
||||
throw util.error(new Error("Header " + key + " contains invalid value"), {
|
||||
code: "InvalidHeader",
|
||||
});
|
||||
}
|
||||
parts.push(key + ":" + this.canonicalHeaderValues(value.toString()));
|
||||
}
|
||||
});
|
||||
return parts.join("\n");
|
||||
}
|
||||
|
||||
canonicalHeaderValues(values) {
|
||||
return values.replace(/\s+/g, " ").replace(/^\s+|\s+$/g, "");
|
||||
}
|
||||
|
||||
authorization(credentials, datetime) {
|
||||
var parts = [];
|
||||
var credString = this.credentialString(datetime);
|
||||
parts.push(this.algorithm + " Credential=" + credentials.accessKeyId + "/" + credString);
|
||||
parts.push("SignedHeaders=" + this.signedHeaders());
|
||||
parts.push("Signature=" + this.signature(credentials, datetime));
|
||||
JDCloud.config.logger("Signature is \n" + JSON.stringify(parts), "DEBUG");
|
||||
return parts.join(", ");
|
||||
}
|
||||
|
||||
hexEncodedHash(string) {
|
||||
return util.crypto.sha256(string, "hex");
|
||||
}
|
||||
|
||||
hexEncodedBodyHash() {
|
||||
let body = this.request.request?.body;
|
||||
if (body && body instanceof ReadableStream) {
|
||||
body = this.request.request?.bodyCache;
|
||||
}
|
||||
return this.hexEncodedHash(body || "");
|
||||
/* var request = this.request;
|
||||
if (this.isPresigned() && this.serviceName === 's3' && !request.body) {
|
||||
return 'UNSIGNED-PAYLOAD';
|
||||
} else if (request.headers['X-Amz-Content-Sha256']) {
|
||||
return request.headers['X-Amz-Content-Sha256'];
|
||||
} else {
|
||||
return this.hexEncodedHash(this.request.body || '');
|
||||
} */
|
||||
}
|
||||
|
||||
isSignableHeader(key) {
|
||||
if (key.toLowerCase().includes("x-jdcloud-")) {
|
||||
return true;
|
||||
}
|
||||
return !this.unsignableHeaders.includes(key.toLowerCase());
|
||||
}
|
||||
};
|
|
@ -0,0 +1,80 @@
|
|||
var cachedSecret = {}
|
||||
var cacheQueue = []
|
||||
var maxCacheEntries = 50
|
||||
var v2Identifier = 'jdcloud2_request'
|
||||
|
||||
var util = require('../util')
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* @api private
|
||||
*
|
||||
* @param date [String]
|
||||
* @param region [String]
|
||||
* @param serviceName [String]
|
||||
* @return [String]
|
||||
*/
|
||||
createScope: function createScope (date, region, serviceName) {
|
||||
return [date.substr(0, 8), region, serviceName, v2Identifier].join('/')
|
||||
},
|
||||
|
||||
/**
|
||||
* @api private
|
||||
*
|
||||
* @param credentials [Credentials]
|
||||
* @param date [String]
|
||||
* @param region [String]
|
||||
* @param service [String]
|
||||
* @param shouldCache [Boolean]
|
||||
* @return [String]
|
||||
*/
|
||||
getSigningKey: function getSigningKey (
|
||||
credentials,
|
||||
date,
|
||||
region,
|
||||
service,
|
||||
shouldCache
|
||||
) {
|
||||
var credsIdentifier = util.crypto.hmac(
|
||||
credentials.secretAccessKey,
|
||||
credentials.accessKeyId,
|
||||
'base64'
|
||||
)
|
||||
var cacheKey = [credsIdentifier, date, region, service].join('_')
|
||||
shouldCache = shouldCache !== false
|
||||
if (shouldCache && cacheKey in cachedSecret) {
|
||||
return cachedSecret[cacheKey]
|
||||
}
|
||||
|
||||
var kDate = util.crypto.hmac(
|
||||
'JDCLOUD2' + credentials.secretAccessKey,
|
||||
date,
|
||||
'buffer'
|
||||
)
|
||||
var kRegion = util.crypto.hmac(kDate, region, 'buffer')
|
||||
var kService = util.crypto.hmac(kRegion, service, 'buffer')
|
||||
|
||||
var signingKey = util.crypto.hmac(kService, v2Identifier, 'buffer')
|
||||
if (shouldCache) {
|
||||
cachedSecret[cacheKey] = signingKey
|
||||
cacheQueue.push(cacheKey)
|
||||
if (cacheQueue.length > maxCacheEntries) {
|
||||
// remove the oldest entry (not the least recently used)
|
||||
delete cachedSecret[cacheQueue.shift()]
|
||||
}
|
||||
}
|
||||
|
||||
return signingKey
|
||||
},
|
||||
|
||||
/**
|
||||
* @api private
|
||||
*
|
||||
* Empties the derived signing key cache. Made available for testing purposes
|
||||
* only.
|
||||
*/
|
||||
emptyCache: function emptyCache () {
|
||||
cachedSecret = {}
|
||||
cacheQueue = []
|
||||
}
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
var util = {
|
||||
isBrowser: function isBrowser() {
|
||||
return process && process.browser;
|
||||
},
|
||||
isNode: function isNode() {
|
||||
return !util.isBrowser();
|
||||
},
|
||||
uriEscape: function uriEscape(string) {
|
||||
var output = encodeURIComponent(string);
|
||||
output = output.replace(/[^A-Za-z0-9_.~\-%]+/g, escape);
|
||||
|
||||
// AWS percent-encodes some extra non-standard characters in a URI
|
||||
output = output.replace(/[*]/g, function (ch) {
|
||||
return "%" + ch.charCodeAt(0).toString(16).toUpperCase();
|
||||
});
|
||||
|
||||
return output;
|
||||
},
|
||||
uriEscapePath: function uriEscapePath(string) {
|
||||
var parts = [];
|
||||
util.arrayEach(string.split("/"), function (part) {
|
||||
parts.push(util.uriEscape(part));
|
||||
});
|
||||
return parts.join("/");
|
||||
},
|
||||
abort: {},
|
||||
each: function each(object, iterFunction) {
|
||||
for (var key in object) {
|
||||
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
||||
var ret = iterFunction.call(this, key, object[key]);
|
||||
if (ret === util.abort) break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
arrayEach: function arrayEach(array, iterFunction) {
|
||||
for (var idx in array) {
|
||||
if (Object.prototype.hasOwnProperty.call(array, idx)) {
|
||||
var ret = iterFunction.call(this, array[idx], parseInt(idx, 10));
|
||||
if (ret === util.abort) break;
|
||||
}
|
||||
}
|
||||
},
|
||||
arraySliceFn: function arraySliceFn(obj) {
|
||||
var fn = obj.slice || obj.webkitSlice || obj.mozSlice;
|
||||
return typeof fn === "function" ? fn : null;
|
||||
},
|
||||
queryParamsToString: function queryParamsToString(params) {
|
||||
var items = [];
|
||||
var escape = util.uriEscape;
|
||||
var sortedKeys = Object.keys(params).sort();
|
||||
|
||||
util.arrayEach(sortedKeys, function (name) {
|
||||
var value = params[name];
|
||||
var ename = escape(name);
|
||||
var result = ename + "=";
|
||||
if (Array.isArray(value)) {
|
||||
var vals = [];
|
||||
util.arrayEach(value, function (item) {
|
||||
vals.push(escape(item));
|
||||
});
|
||||
result = ename + "=" + vals.sort().join("&" + ename + "=");
|
||||
} else if (value !== undefined && value !== null) {
|
||||
result = ename + "=" + escape(value);
|
||||
}
|
||||
items.push(result);
|
||||
});
|
||||
|
||||
return items.join("&");
|
||||
},
|
||||
date: {
|
||||
getDate() {
|
||||
return new Date();
|
||||
},
|
||||
iso8601: function iso8601(date) {
|
||||
if (date === undefined) {
|
||||
date = util.date.getDate();
|
||||
}
|
||||
return date.toISOString().replace(/\.\d{3}Z$/, "Z");
|
||||
},
|
||||
},
|
||||
crypto: {
|
||||
/* eslint-disable no-use-before-define */
|
||||
crc32Table: [
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
],
|
||||
/* eslint-disable no-use-before-define */
|
||||
|
||||
crc32: function crc32(data) {
|
||||
var tbl = util.crypto.crc32Table;
|
||||
var crc = 0 ^ -1;
|
||||
|
||||
if (typeof data === "string") {
|
||||
data = new util.Buffer(data);
|
||||
}
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var code = data.readUInt8(i);
|
||||
crc = (crc >>> 8) ^ tbl[(crc ^ code) & 0xff];
|
||||
}
|
||||
return (crc ^ -1) >>> 0;
|
||||
},
|
||||
|
||||
hmac: function hmac(key, string, digest, fn) {
|
||||
if (!digest) digest = "binary";
|
||||
if (digest === "buffer") {
|
||||
digest = undefined;
|
||||
}
|
||||
if (!fn) fn = "sha256";
|
||||
if (typeof string === "string") string = new util.Buffer(string);
|
||||
return util.crypto.lib.createHmac(fn, key).update(string).digest(digest);
|
||||
},
|
||||
|
||||
md5: function md5(data, digest, callback) {
|
||||
return util.crypto.hash("md5", data, digest, callback);
|
||||
},
|
||||
|
||||
sha256: function sha256(data, digest, callback) {
|
||||
return util.crypto.hash("sha256", data, digest, callback);
|
||||
},
|
||||
|
||||
hash: function (algorithm, data, digest, callback) {
|
||||
var hash = util.crypto.createHash(algorithm);
|
||||
if (!digest) {
|
||||
digest = "binary";
|
||||
}
|
||||
if (digest === "buffer") {
|
||||
digest = undefined;
|
||||
}
|
||||
if (typeof data === "string") data = new util.Buffer(data);
|
||||
var sliceFn = util.arraySliceFn(data);
|
||||
var isBuffer = util.Buffer.isBuffer(data);
|
||||
// Identifying objects with an ArrayBuffer as buffers
|
||||
if (util.isBrowser() && typeof ArrayBuffer !== "undefined" && data && data.buffer instanceof ArrayBuffer) {
|
||||
isBuffer = true;
|
||||
}
|
||||
|
||||
if (callback && typeof data === "object" && typeof data.on === "function" && !isBuffer) {
|
||||
data.on("data", function (chunk) {
|
||||
hash.update(chunk);
|
||||
});
|
||||
data.on("error", function (err) {
|
||||
callback(err);
|
||||
});
|
||||
data.on("end", function () {
|
||||
callback(null, hash.digest(digest));
|
||||
});
|
||||
} else if (callback && sliceFn && !isBuffer && typeof FileReader !== "undefined") {
|
||||
// this might be a File/Blob
|
||||
var index = 0;
|
||||
var size = 1024 * 512;
|
||||
var reader = new FileReader();
|
||||
reader.onerror = function () {
|
||||
callback(new Error("Failed to read data."));
|
||||
};
|
||||
reader.onload = function () {
|
||||
var buf = new util.Buffer(new Uint8Array(reader.result));
|
||||
hash.update(buf);
|
||||
index += buf.length;
|
||||
reader._continueReading();
|
||||
};
|
||||
reader._continueReading = function () {
|
||||
if (index >= data.size) {
|
||||
callback(null, hash.digest(digest));
|
||||
return;
|
||||
}
|
||||
|
||||
var back = index + size;
|
||||
if (back > data.size) back = data.size;
|
||||
reader.readAsArrayBuffer(sliceFn.call(data, index, back));
|
||||
};
|
||||
|
||||
reader._continueReading();
|
||||
} else {
|
||||
if (util.isBrowser() && typeof data === "object" && !isBuffer) {
|
||||
data = new util.Buffer(new Uint8Array(data));
|
||||
}
|
||||
var out = hash.update(data).digest(digest);
|
||||
if (callback) callback(null, out);
|
||||
return out;
|
||||
}
|
||||
},
|
||||
|
||||
toHex: function toHex(data) {
|
||||
var out = [];
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
out.push(("0" + data.charCodeAt(i).toString(16)).substr(-2, 2));
|
||||
}
|
||||
return out.join("");
|
||||
},
|
||||
|
||||
createHash: function createHash(algorithm) {
|
||||
return util.crypto.lib.createHash(algorithm);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = util;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"inlineSourceMap":true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"stripInternal": true,
|
||||
"skipLibCheck": true,
|
||||
"pretty": true,
|
||||
"declaration": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"typeRoots": [ "./typings", "./node_modules/@types"],
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"composite": true,
|
||||
"useDefineForClassFields": true,
|
||||
"strict": false,
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": false,
|
||||
"lib": ["ESNext", "DOM"],
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.js",
|
||||
"src/**/*.json"
|
||||
],
|
||||
"exclude": [
|
||||
"*.ts",
|
||||
"dist",
|
||||
"node_modules",
|
||||
"test"
|
||||
],
|
||||
}
|
|
@ -12,6 +12,7 @@ export type CertInfo = {
|
|||
crt: string;
|
||||
key: string;
|
||||
csr: string;
|
||||
ic?: string;
|
||||
pfx?: string;
|
||||
der?: string;
|
||||
};
|
||||
|
@ -243,13 +244,25 @@ export class AcmeService {
|
|||
if (privateKeyArr.length > 1) {
|
||||
size = parseInt(privateKeyArr[1]);
|
||||
}
|
||||
|
||||
let encodingType = "pkcs8";
|
||||
if (privateKeyArr.length > 2) {
|
||||
encodingType = privateKeyArr[2];
|
||||
}
|
||||
|
||||
if (type == "ec") {
|
||||
const name: any = "P-" + size;
|
||||
privateKey = await acme.crypto.createPrivateEcdsaKey(name);
|
||||
privateKey = await acme.crypto.createPrivateEcdsaKey(name, encodingType);
|
||||
} else {
|
||||
privateKey = await acme.crypto.createPrivateRsaKey(size);
|
||||
privateKey = await acme.crypto.createPrivateRsaKey(size, encodingType);
|
||||
}
|
||||
const [key, csr] = await acme.crypto.createCsr(
|
||||
|
||||
let createCsr: any = acme.crypto.createCsr;
|
||||
if (encodingType === "pkcs1") {
|
||||
//兼容老版本
|
||||
createCsr = acme.forge.createCsr;
|
||||
}
|
||||
const [key, csr] = await createCsr(
|
||||
{
|
||||
commonName,
|
||||
...csrInfo,
|
||||
|
@ -257,6 +270,7 @@ export class AcmeService {
|
|||
},
|
||||
privateKey
|
||||
);
|
||||
|
||||
if (dnsProvider == null) {
|
||||
throw new Error("dnsProvider 不能为空");
|
||||
}
|
||||
|
@ -276,8 +290,9 @@ export class AcmeService {
|
|||
signal: this.options.signal,
|
||||
});
|
||||
|
||||
const crtString = crt.toString();
|
||||
const cert: CertInfo = {
|
||||
crt: crt.toString(),
|
||||
crt: crtString,
|
||||
key: key.toString(),
|
||||
csr: csr.toString(),
|
||||
};
|
||||
|
|
|
@ -175,6 +175,7 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
|
|||
const zip = new JSZip();
|
||||
zip.file("cert.crt", cert.crt);
|
||||
zip.file("cert.key", cert.key);
|
||||
zip.file("intermediate.crt", cert.ic);
|
||||
if (cert.pfx) {
|
||||
zip.file("cert.pfx", Buffer.from(cert.pfx, "base64"));
|
||||
}
|
||||
|
|
|
@ -6,7 +6,14 @@ import { crypto } from "@certd/acme-client";
|
|||
import { ILogger } from "@certd/pipeline";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export type CertReaderHandleContext = { reader: CertReader; tmpCrtPath: string; tmpKeyPath: string; tmpPfxPath?: string; tmpDerPath?: string };
|
||||
export type CertReaderHandleContext = {
|
||||
reader: CertReader;
|
||||
tmpCrtPath: string;
|
||||
tmpKeyPath: string;
|
||||
tmpPfxPath?: string;
|
||||
tmpDerPath?: string;
|
||||
tmpIcPath?: string;
|
||||
};
|
||||
export type CertReaderHandle = (ctx: CertReaderHandleContext) => Promise<void>;
|
||||
export type HandleOpts = { logger: ILogger; handle: CertReaderHandle };
|
||||
export class CertReader {
|
||||
|
@ -14,6 +21,7 @@ export class CertReader {
|
|||
crt: string;
|
||||
key: string;
|
||||
csr: string;
|
||||
ic: string; //中间证书
|
||||
|
||||
detail: any;
|
||||
expires: number;
|
||||
|
@ -23,11 +31,30 @@ export class CertReader {
|
|||
this.key = certInfo.key;
|
||||
this.csr = certInfo.csr;
|
||||
|
||||
this.ic = certInfo.ic;
|
||||
if (!this.ic) {
|
||||
this.ic = this.getIc();
|
||||
this.cert.ic = this.ic;
|
||||
}
|
||||
|
||||
const { detail, expires } = this.getCrtDetail(this.cert.crt);
|
||||
this.detail = detail;
|
||||
this.expires = expires.getTime();
|
||||
}
|
||||
|
||||
getIc() {
|
||||
//中间证书ic, 就是crt的第一个 -----END CERTIFICATE----- 之后的内容
|
||||
const endStr = "-----END CERTIFICATE-----";
|
||||
const firstBlockEndIndex = this.crt.indexOf(endStr);
|
||||
|
||||
const start = firstBlockEndIndex + endStr.length + 1;
|
||||
if (this.crt.length <= start) {
|
||||
return "";
|
||||
}
|
||||
const ic = this.crt.substring(start);
|
||||
return ic.trim();
|
||||
}
|
||||
|
||||
toCertInfo(): CertInfo {
|
||||
return this.cert;
|
||||
}
|
||||
|
@ -38,7 +65,7 @@ export class CertReader {
|
|||
return { detail, expires };
|
||||
}
|
||||
|
||||
saveToFile(type: "crt" | "key" | "pfx" | "der", filepath?: string) {
|
||||
saveToFile(type: "crt" | "key" | "pfx" | "der" | "ic", filepath?: string) {
|
||||
if (!this.cert[type]) {
|
||||
return;
|
||||
}
|
||||
|
@ -52,7 +79,7 @@ export class CertReader {
|
|||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
if (type === "crt" || type === "key") {
|
||||
if (type === "crt" || type === "key" || type === "ic") {
|
||||
fs.writeFileSync(filepath, this.cert[type]);
|
||||
} else {
|
||||
fs.writeFileSync(filepath, Buffer.from(this.cert[type], "base64"));
|
||||
|
@ -66,8 +93,9 @@ export class CertReader {
|
|||
const tmpCrtPath = this.saveToFile("crt");
|
||||
const tmpKeyPath = this.saveToFile("key");
|
||||
const tmpPfxPath = this.saveToFile("pfx");
|
||||
const tmpDerPath = this.saveToFile("der");
|
||||
const tmpIcPath = this.saveToFile("ic");
|
||||
logger.info("本地文件写入成功");
|
||||
const tmpDerPath = this.saveToFile("der");
|
||||
try {
|
||||
return await opts.handle({
|
||||
reader: this,
|
||||
|
@ -75,6 +103,7 @@ export class CertReader {
|
|||
tmpKeyPath: tmpKeyPath,
|
||||
tmpPfxPath: tmpPfxPath,
|
||||
tmpDerPath: tmpDerPath,
|
||||
tmpIcPath: tmpIcPath,
|
||||
});
|
||||
} catch (err) {
|
||||
throw err;
|
||||
|
@ -90,6 +119,7 @@ export class CertReader {
|
|||
removeFile(tmpKeyPath);
|
||||
removeFile(tmpPfxPath);
|
||||
removeFile(tmpDerPath);
|
||||
removeFile(tmpIcPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
|||
{ value: "rsa_2048", label: "RSA 2048" },
|
||||
{ value: "rsa_3072", label: "RSA 3072" },
|
||||
{ value: "rsa_4096", label: "RSA 4096" },
|
||||
{ value: "rsa_2048_pkcs1", label: "RSA 2048 pkcs1 (旧版)" },
|
||||
{ value: "ec_256", label: "EC 256" },
|
||||
{ value: "ec_384", label: "EC 384" },
|
||||
// { value: "ec_521", label: "EC 521" },
|
||||
|
@ -195,8 +196,8 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
|||
const certInfo = this.formatCerts(cert);
|
||||
return new CertReader(certInfo);
|
||||
} catch (e: any) {
|
||||
const message: string = e.message;
|
||||
if (message.indexOf("redundant with a wildcard domain in the same request") >= 0) {
|
||||
const message: string = e?.message;
|
||||
if (message != null && message.indexOf("redundant with a wildcard domain in the same request") >= 0) {
|
||||
this.logger.error(e);
|
||||
throw new Error(`通配符域名已经包含了普通域名,请删除其中一个(${message})`);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,6 @@ VITE_APP_SLOGAN=让你的证书永不过期
|
|||
VITE_APP_COPYRIGHT_YEAR=2021-2024
|
||||
VITE_APP_COPYRIGHT_NAME=handsfree.work
|
||||
VITE_APP_COPYRIGHT_URL=https://certd.handsfree.work
|
||||
VITE_APP_LOGO_PATH=./images/logo/logo.svg
|
||||
VITE_APP_LOGO=./images/logo/logo.svg
|
||||
VITE_APP_PROJECT_PATH=https://github.com/certd/certd
|
||||
VITE_APP_ICP_NO=
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/lib-iframe": "^1.24.4",
|
||||
"@certd/pipeline": "^1.24.4",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
|
|
|
@ -54,6 +54,12 @@
|
|||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">qiniuyun</div>
|
||||
<div class="code-name">&#xe603;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">aliyun</div>
|
||||
|
@ -96,7 +102,7 @@
|
|||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.svg?t=1726734453480#iconfont') format('svg');
|
||||
src: url('iconfont.svg?t=1727153857332#iconfont') format('svg');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
|
@ -122,6 +128,15 @@
|
|||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-qiniuyun"></span>
|
||||
<div class="name">
|
||||
qiniuyun
|
||||
</div>
|
||||
<div class="code-name">.icon-qiniuyun
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-aliyun"></span>
|
||||
<div class="name">
|
||||
|
@ -185,6 +200,14 @@
|
|||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-qiniuyun"></use>
|
||||
</svg>
|
||||
<div class="name">qiniuyun</div>
|
||||
<div class="code-name">#icon-qiniuyun</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-aliyun"></use>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4688792 */
|
||||
src: url('iconfont.svg?t=1726734453480#iconfont') format('svg');
|
||||
src: url('iconfont.svg?t=1727153857332#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
|
@ -11,6 +11,10 @@
|
|||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-qiniuyun:before {
|
||||
content: "\e603";
|
||||
}
|
||||
|
||||
.icon-aliyun:before {
|
||||
content: "\e601";
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,6 +5,13 @@
|
|||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "9612999",
|
||||
"name": "qiniuyun",
|
||||
"font_class": "qiniuyun",
|
||||
"unicode": "e603",
|
||||
"unicode_decimal": 58883
|
||||
},
|
||||
{
|
||||
"icon_id": "26492886",
|
||||
"name": "aliyun",
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
/>
|
||||
<missing-glyph />
|
||||
|
||||
<glyph glyph-name="qiniuyun" unicode="" d="M53.684427 804.418697a705.73155 705.73155 0 0 1 52.395618-94.012709 810.709913 810.709913 0 0 1 74.626331-96.63249c6.100347-11.003079 18.301041-15.868388 24.438812-28.106506 10.965654-9.76804 21.968734-20.733695 32.971814-31.736775a1023.698102 1023.698102 0 0 1 119.761413-86.714748c17.103427-9.805465 33.009239-18.338467 50.112667-26.871467 3.742544-4.902733 1.197614-9.805465 2.432653-14.670773 6.100347-35.441893 8.533-72.0814 13.435733-107.485868 2.432654-20.77112 3.742544-42.777279 7.297962-63.623251 6.137772-35.441893 9.805465-72.0814 14.670773-107.523293 6.100347-40.3072 9.76804-80.576976 17.103427-120.884176 3.742544-19.573506 14.670773-35.441893 23.203773-53.78036 2.432654-3.742544 6.100347-6.100347 8.570427-10.965654 31.736774-40.3072 74.476628-57.410627 125.786909-57.410627h287.053136c73.279014 0 124.589295 35.404468 152.695801 103.818174 13.435733 32.971814 15.868388 68.413707 19.536081 102.620562l25.636427 208.871388c2.470079 10.965654-1.197614 12.200694-10.965654 12.200695-24.438814 0-46.444972-6.100347-68.413707-14.670774-17.103427-4.865307-28.106507-17.103427-40.3072-28.106507a21.220225 21.220225 0 0 1-8.533002-6.100347c-8.570426-13.435733-19.573506-24.438814-24.438813-39.07216a410.819071 410.819071 0 0 1-14.670773-41.54224c-9.76804-34.169428-19.536081-69.611321-29.304121-105.015789-3.742544-17.103427-13.435733-29.341546-24.438813-42.77728a40.906008 40.906008 0 0 0-29.304121-13.435734c-59.880706-3.742544-118.488948-2.432654-178.332228-1.197614a58.159136 58.159136 0 0 0-50.112667 30.501735c-8.533 14.670773-12.200694 29.341546-15.868387 45.209934-4.865307 22.00616-14.633347 42.739854-17.103426 65.981053a11.227632 11.227632 0 0 0-1.197615 6.062921c-17.103427 63.623251-35.441893 125.824334-52.395618 189.335309-1.197614 3.742544-3.742544 7.485088-1.197614 9.768041 2.432654 3.742544 7.297961 1.23504 10.965654 0a665.124946 665.124946 0 0 1 70.846362-14.633348 811.907528 811.907528 0 0 1 204.006081-6.100347 435.557288 435.557288 0 0 1 78.181747 13.435733c20.77112 8.533 42.739854 12.200694 64.708589 18.301041 28.106507 8.570426 54.977974 19.536081 83.084479 29.341547 12.200694 6.100347 25.636428 12.200694 37.837122 18.30104a839.826907 839.826907 0 0 1 90.419867 53.742934c4.865307 3.742544 9.76804 9.76804 14.633347 12.23812 13.435733 7.485088 24.438814 17.066002 36.639507 25.636427a52.395618 52.395618 0 0 1 20.771121 15.868387c6.100347 8.570426 14.670773 14.670773 22.00616 21.968735s15.868388 13.473159 21.968733 22.006159c6.100347 7.485088 14.670773 14.670773 22.00616 21.968734s7.485088 17.140853 17.103427 22.00616c7.485088 4.865307 10.965654 13.435733 17.103426 19.53608 2.432654 2.432654 4.865307 3.742544 6.100347 6.137773 1.197614 3.742544 3.742544 6.100347 4.865308 9.76804 14.670773 17.103427 25.673853 37.837121 39.109586 57.373202 10.965654 15.905812 17.103427 33.009239 28.069082 47.680012 1.23504 3.742544 0 7.485088 2.470079 10.965655 13.435733 18.338467 19.536081 40.3072 28.069081 61.07832 3.742544 7.485088 1.23504 9.76804-6.100348 9.76804-8.533 1.23504-17.103427 2.432654-23.203773-4.902732-1.23504-2.432654-3.742544-2.432654-6.100347-2.432654a15.419282 15.419282 0 0 1-15.868387-8.533001c-8.570426-14.670773-26.908893-23.203774-30.539161-41.54224h-1.235039c-8.570426 3.742544-12.200694-3.742544-15.868388-8.533a714.825933 714.825933 0 0 0-72.0814-64.746014 748.92051 748.92051 0 0 0-63.62325-45.209934c-28.106507-17.103427-53.742934-35.404468-84.282095-47.605161-1.23504-1.23504-2.432654-2.470079-2.432654-3.742544 0-3.742544-2.470079-2.470079-4.902732-2.470079a17.178278 17.178278 0 0 1-7.485088-2.432655c-32.971814-19.536081-69.611321-32.971814-105.015789-45.209933-50.112667-18.301041-103.8556-29.304121-156.363494-36.639507a510.370746 510.370746 0 0 0-74.514055-6.100347c-59.880706 0-118.488948-2.432654-178.332228 8.570426a1135.637597 1135.637597 0 0 0-124.589295 28.069082 8.944681 8.944681 0 0 0-7.485089 8.570425 134.057932 134.057932 0 0 1-8.570426 31.736775c-12.200694 40.3072-17.066002 83.047055-39.072161 120.921601-1.23504 1.23504-2.432654 1.23504-4.902732 1.23504a1.197614 1.197614 0 0 1-1.197614-1.23504v2.470079c-8.570426 11.003079-22.00616 15.868388-35.404468 18.301042-15.905812 3.742544-20.77112-1.197614-18.338467-15.868388 4.902733-37.874547 8.533-76.946708 14.633348-116.018869 1.23504-2.432654 2.470079-6.100347-1.197614-7.297961a7.709641 7.709641 0 0 0-7.485089 0c-17.103427 9.76804-32.971814 18.301041-50.07524 26.871467-23.203774 10.965654-43.974893 25.636428-64.746014 39.072161-9.76804 4.902733-17.103427 12.200694-25.636427 18.301041a620.438969 620.438969 0 0 0-81.849441 67.178668 444.988499 444.988499 0 0 0-67.365795 68.413707A49.888114 49.888114 0 0 1 47.022699 862.802386C32.38935 862.802386 28.721657 857.899653 34.822004 844.463919z" horiz-adv-x="1525" />
|
||||
|
||||
<glyph glyph-name="aliyun" unicode="" d="M991.142857 521.25c-0.321428 87.964285-71.678572 159.214285-159.75 159.214285H580.464285l23.142858-91.285713 215.357142-46.821429a45.621428 45.621428 0 0 0 35.25-42.535715c0.107143-0.535715 0.107143-231.535715 0-232.071428a45.621428 45.621428 0 0 0-35.25-42.535715l-215.357142-46.821428-23.142858-91.392857h250.928572c87.964285 0 159.428572 71.25 159.75 159.214285V521.25zM205.035715 225a45.621428 45.621428 0 0 0-35.25 42.535715c-0.107143 0.642857-0.107143 231.535715 0 232.071428 0.857143 20.785715 15.642857 38.035715 35.25 42.535714l215.357142 46.821428 23.142858 91.285715H192.5c-87.964285 0-159.428572-71.142857-159.75-159.214285V245.785715c0.321428-87.964285 71.785715-159.214285 159.75-159.214287H443.428572l-23.142857 91.392857-215.25 47.035715z m215.25 170.142857h183.214285v-22.821429h-183.214285z" horiz-adv-x="1024" />
|
||||
|
||||
<glyph glyph-name="tencentcloud" unicode="" d="M1224.103058 83.777804c-22.109216-22.109216-66.325973-55.270527-143.704042-55.270528H605.068459c143.702366 138.177156 265.300542 254.245096 276.354312 265.300542 11.05377 11.05377 38.689872 38.688196 66.324298 60.797413 55.272203 49.745318 99.48896 55.270527 138.177156 55.270527 55.272203 0 99.487284-22.107541 138.178833-55.270527 77.378068-71.852859 77.378068-198.974569 0-270.827427m93.960399 359.259265c-55.272203 60.799088-138.178832 99.48896-226.612346 99.48896-77.378068 0-143.702366-27.634426-204.501454-71.852858-22.109216-22.107541-55.270527-44.216757-82.906629-77.378068-22.107541-22.107541-497.436422-486.384328-497.436422-486.384328 27.634426-5.526885 60.799088-5.526885 88.433514-5.526885h602.450591c44.216757 0 77.378068 0 110.542731 5.526885 71.851183 5.526885 143.702366 33.164663 204.501454 88.433514 127.123386 121.596501 127.123386 326.099631 5.528561 447.694456zM544.271046 476.200056c-60.799088 44.216757-121.598177 66.325973-193.449359 66.325973-88.433514 0-171.338467-38.689872-226.608995-99.487284-121.598177-127.125062-121.598177-326.099631 5.52521-453.221341 55.272203-49.743642 110.542731-77.378068 176.867028-82.906629l127.125062 121.596501h-71.852859c-71.852859 5.526885-116.069616 27.634426-143.704041 55.270528-77.379744 77.378068-77.379744 198.974569-5.528561 276.354312 38.689872 38.689872 82.906629 55.270527 138.177156 55.270527 33.164663 0 82.906629-5.526885 132.651947-55.268851 22.105865-22.109216 82.904953-66.325973 105.012494-88.433514h5.528561l82.904953 82.903277v5.530237c-38.688196 38.688196-99.48896 88.431838-132.648596 116.06794M1124.614097 614.377212C1063.816685 780.193822 903.530312 896.25841 721.138075 896.25841c-215.558576 0-386.897043-160.284697-420.06003-359.259266 16.582331 0 33.164663 5.526885 55.272203 5.526885s49.743642-5.526885 71.851183-5.526885c27.634426 138.177156 149.229251 237.666117 292.934968 237.666117 121.596501-0.001676 226.61067-71.852859 276.354312-176.867028 0 0 5.528561-5.526885 5.528561 0 38.688196 5.526885 82.904953 16.580655 121.594825 16.580655 0-5.526885 0-5.526885 0 0" horiz-adv-x="1436" />
|
||||
|
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 16 KiB |
|
@ -22,3 +22,18 @@ export async function getInstallInfo(): Promise<SysInstallInfo> {
|
|||
method: "get"
|
||||
});
|
||||
}
|
||||
|
||||
export async function getSiteInfo(): Promise<SysInstallInfo> {
|
||||
return await request({
|
||||
url: "/basic/settings/siteInfo",
|
||||
method: "get"
|
||||
});
|
||||
}
|
||||
|
||||
export async function bindUrl(data): Promise<SysInstallInfo> {
|
||||
return await request({
|
||||
url: "/sys/plus/bindUrl",
|
||||
method: "post",
|
||||
data
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import { CheckCircleOutlined, InfoCircleOutlined, UndoOutlined } from "@ant-desi
|
|||
import CronEditor from "./cron-editor/index.vue";
|
||||
import { CronLight } from "@vue-js-cron/light";
|
||||
import "@vue-js-cron/light/dist/light.css";
|
||||
import Plugins from "./plugins/index";
|
||||
export default {
|
||||
install(app: any) {
|
||||
app.component("PiContainer", PiContainer);
|
||||
|
@ -24,5 +25,6 @@ export default {
|
|||
app.component("UndoOutlined", UndoOutlined);
|
||||
|
||||
app.use(vip);
|
||||
app.use(Plugins);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import PiSynologyIdDeviceGetter from "./synology/device-id-getter.vue";
|
||||
export default {
|
||||
install(app: any) {
|
||||
app.component("PiSynologyDeviceIdGetter", PiSynologyIdDeviceGetter);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<div>
|
||||
<contextHolder />
|
||||
<a-input :value="value" :allow-clear="true" @update:value="emit('update:value', $event)">
|
||||
<template #suffix>
|
||||
<a-tag class="cursor-pointer" @click="getDeviceId">获取设备ID</a-tag>
|
||||
</template>
|
||||
</a-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import { defineProps, ref, useAttrs } from "vue";
|
||||
import { request } from "/@/api/service";
|
||||
import { Modal } from "ant-design-vue";
|
||||
|
||||
const props = defineProps<{
|
||||
type: string;
|
||||
typeName: string;
|
||||
form: any;
|
||||
value?: any;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:value": any;
|
||||
}>();
|
||||
|
||||
const attrs = useAttrs();
|
||||
|
||||
const otpCodeRef = ref("");
|
||||
|
||||
async function doRequest(action: string, data: any) {
|
||||
const res = await request({
|
||||
url: "/pi/handle",
|
||||
method: "post",
|
||||
data: {
|
||||
type: props.type,
|
||||
typeName: props.typeName,
|
||||
action,
|
||||
data: data,
|
||||
input: props.form.access
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
async function loginWithOTPCode(otpCode: string) {
|
||||
return await doRequest("LoginWithOPTCode", {
|
||||
otpCode
|
||||
});
|
||||
}
|
||||
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
async function getDeviceId() {
|
||||
//打开对话框
|
||||
|
||||
modal.confirm({
|
||||
title: "请输入OTP验证码",
|
||||
content: () => {
|
||||
return (
|
||||
<a-form-item-rest>
|
||||
<a-input v-model:value={otpCodeRef.value} placeholder="请输入OTP验证码" />
|
||||
</a-form-item-rest>
|
||||
);
|
||||
},
|
||||
onOk: async () => {
|
||||
const res = await loginWithOTPCode(otpCodeRef.value);
|
||||
console.log("did返回", res);
|
||||
emit("update:value", res.did);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
|
@ -18,6 +18,7 @@ import dayjs from "dayjs";
|
|||
import { message, Modal } from "ant-design-vue";
|
||||
import * as api from "./api";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
@ -90,6 +91,7 @@ const formState = reactive({
|
|||
code: ""
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
async function doActive() {
|
||||
if (!formState.code) {
|
||||
message.error("请输入激活码");
|
||||
|
@ -100,7 +102,19 @@ async function doActive() {
|
|||
await userStore.reInit();
|
||||
Modal.success({
|
||||
title: "激活成功",
|
||||
content: `您已成功激活专业版,有效期至:${dayjs(userStore.plusInfo.expireTime).format("YYYY-MM-DD")}`
|
||||
content: `您已成功激活专业版,有效期至:${dayjs(userStore.plusInfo.expireTime).format("YYYY-MM-DD")}`,
|
||||
onOk() {
|
||||
if (!(settingStore.installInfo.bindUserId > 0)) {
|
||||
//未绑定账号
|
||||
Modal.confirm({
|
||||
title: "是否绑定袖手账号",
|
||||
content: "绑定账号后,可以避免专业版License丢失,强烈建议绑定",
|
||||
onOk() {
|
||||
router.push("/sys/account");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +146,7 @@ function openUpgrade() {
|
|||
<li>可加VIP群,需求优先实现</li>
|
||||
<li>证书流水线数量无限制(免费版限制10条)</li>
|
||||
<li>免配置发邮件功能</li>
|
||||
<li>FTP上传、cdnfly、宝塔、易盾等部署插件</li>
|
||||
<li>FTP上传、cdnfly、宝塔、易盾、群晖等部署插件</li>
|
||||
<li>更多特权敬请期待</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -143,7 +157,6 @@ function openUpgrade() {
|
|||
<div class="flex-o w-100">
|
||||
<span>站点ID:</span>
|
||||
<fs-copyable class="flex-1" v-model={computedSiteId.value}></fs-copyable>
|
||||
<div>注意保存好数据库,暂不支持换绑(默认数据库路径/data/certd/db.sqlite)</div>
|
||||
</div>
|
||||
<a-input class="mt-10" v-model:value={formState.code} placeholder={placeholder} />
|
||||
</div>
|
||||
|
|
|
@ -57,7 +57,7 @@ export default {
|
|||
.container {
|
||||
.main {
|
||||
max-width: 368px;
|
||||
width: 98%;
|
||||
width: 96%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -136,16 +136,15 @@ export default {
|
|||
}
|
||||
|
||||
.main {
|
||||
min-width: 260px;
|
||||
width: 368px;
|
||||
margin: 0 auto;
|
||||
min-width: 300px;
|
||||
width: 94%;
|
||||
}
|
||||
|
||||
.footer {
|
||||
// position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
margin: 48px 0 24px;
|
||||
margin: 24px 0 24px;
|
||||
text-align: center;
|
||||
|
||||
.links {
|
||||
|
@ -163,6 +162,7 @@ export default {
|
|||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
span {
|
||||
|
|
|
@ -65,6 +65,16 @@ export const sysResources = [
|
|||
},
|
||||
path: "/sys/settings",
|
||||
component: "/sys/settings/index.vue"
|
||||
},
|
||||
{
|
||||
title: "账号绑定",
|
||||
name: "account",
|
||||
meta: {
|
||||
icon: "ion:golf-outline",
|
||||
permission: "sys:settings:view"
|
||||
},
|
||||
path: "/sys/account",
|
||||
component: "/sys/account/index.vue"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { theme } from "ant-design-vue";
|
||||
import { Modal, theme } from "ant-design-vue";
|
||||
import _ from "lodash-es";
|
||||
// @ts-ignore
|
||||
import { LocalStorage } from "/src/utils/util.storage";
|
||||
|
||||
import * as basicApi from "/@/api/modules/api.basic";
|
||||
import { SysInstallInfo, SysPublicSetting } from "/@/api/modules/api.basic";
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
import { mitter } from "/@/utils/util.mitt";
|
||||
|
||||
export type ThemeToken = {
|
||||
token: {
|
||||
|
@ -23,6 +25,17 @@ export interface SettingState {
|
|||
sysPublic?: SysPublicSetting;
|
||||
installInfo?: {
|
||||
siteId: string;
|
||||
installTime?: number;
|
||||
bindUserId?: number;
|
||||
bindUrl?: string;
|
||||
accountServerBaseUrl?: string;
|
||||
appKey?: string;
|
||||
};
|
||||
siteInfo?: {
|
||||
TITLE: string;
|
||||
SLOGAN: string;
|
||||
LOGO: string;
|
||||
ICP_NO: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -44,7 +57,17 @@ export const useSettingStore = defineStore({
|
|||
managerOtherUserPipeline: false
|
||||
},
|
||||
installInfo: {
|
||||
siteId: ""
|
||||
siteId: "",
|
||||
bindUserId: null,
|
||||
bindUrl: "",
|
||||
accountServerBaseUrl: "",
|
||||
appKey: ""
|
||||
},
|
||||
siteInfo: {
|
||||
TITLE: "",
|
||||
SLOGAN: "",
|
||||
LOGO: "",
|
||||
ICP_NO: ""
|
||||
}
|
||||
}),
|
||||
getters: {
|
||||
|
@ -63,9 +86,59 @@ export const useSettingStore = defineStore({
|
|||
const settings = await basicApi.getSysPublicSettings();
|
||||
_.merge(this.sysPublic, settings);
|
||||
|
||||
const siteInfo = await basicApi.getSiteInfo();
|
||||
_.merge(this.siteInfo, siteInfo);
|
||||
|
||||
await this.loadInstallInfo();
|
||||
|
||||
await this.checkUrlBound();
|
||||
},
|
||||
async loadInstallInfo() {
|
||||
const installInfo = await basicApi.getInstallInfo();
|
||||
_.merge(this.installInfo, installInfo);
|
||||
},
|
||||
async checkUrlBound() {
|
||||
const userStore = useUserStore();
|
||||
if (!userStore.isAdmin || !userStore.isPlus) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bindUrl = this.installInfo.bindUrl;
|
||||
|
||||
function getBaseUrl() {
|
||||
let url = window.location.href;
|
||||
//只要hash前面的部分
|
||||
url = url.split("#")[0];
|
||||
return url;
|
||||
}
|
||||
|
||||
const doBindUrl = async (url: string) => {
|
||||
await basicApi.bindUrl({ url });
|
||||
await this.loadInstallInfo();
|
||||
};
|
||||
const baseUrl = getBaseUrl();
|
||||
if (!bindUrl) {
|
||||
//绑定url
|
||||
await doBindUrl(baseUrl);
|
||||
} else {
|
||||
//检查当前url 是否与绑定的url一致
|
||||
const url = window.location.href;
|
||||
if (!url.startsWith(bindUrl)) {
|
||||
Modal.confirm({
|
||||
title: "URL地址有变化",
|
||||
content: "以后都用这个新地址访问本系统吗?",
|
||||
onOk: async () => {
|
||||
await doBindUrl(baseUrl);
|
||||
},
|
||||
okText: "是的,继续",
|
||||
cancelText: "不是,回到原来的地址",
|
||||
onCancel: () => {
|
||||
window.location.href = bindUrl;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
persistThemeConfig() {
|
||||
LocalStorage.set(SETTING_THEME_KEY, this.getThemeConfig);
|
||||
},
|
||||
|
@ -108,3 +181,7 @@ export const useSettingStore = defineStore({
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
mitter.on("app.login", async () => {
|
||||
await useSettingStore().init();
|
||||
});
|
||||
|
|
|
@ -1,37 +1,25 @@
|
|||
// @ts-ignore
|
||||
import _ from "lodash-es";
|
||||
export function getEnvValue(key: string) {
|
||||
// @ts-ignore
|
||||
return import.meta.env["VITE_APP_" + key];
|
||||
}
|
||||
|
||||
export class EnvConfig {
|
||||
API: string;
|
||||
MODE: string;
|
||||
STORAGE: string;
|
||||
TITLE: string;
|
||||
SLOGAN: string;
|
||||
COPYRIGHT_YEAR: string;
|
||||
COPYRIGHT_NAME: string;
|
||||
COPYRIGHT_URL: string;
|
||||
LOGO_PATH: string;
|
||||
PM_ENABLED: string;
|
||||
ICP_NO: string;
|
||||
constructor() {
|
||||
this.init();
|
||||
}
|
||||
MODE: string = import.meta.env.MODE;
|
||||
API: string = import.meta.env.VITE_APP_API;
|
||||
STORAGE: string = import.meta.env.VITE_APP_STORAGE;
|
||||
TITLE: string = import.meta.env.VITE_APP_TITLE;
|
||||
SLOGAN: string = import.meta.env.VITE_APP_SLOGAN;
|
||||
COPYRIGHT_YEAR: string = import.meta.env.VITE_APP_COPYRIGHT_YEAR;
|
||||
COPYRIGHT_NAME: string = import.meta.env.VITE_APP_COPYRIGHT_NAME;
|
||||
COPYRIGHT_URL: string = import.meta.env.VITE_APP_COPYRIGHT_URL;
|
||||
LOGO: string = import.meta.env.VITE_APP_LOGO;
|
||||
PM_ENABLED: string = import.meta.env.VITE_APP_PM_ENABLED;
|
||||
ICP_NO: string = import.meta.env.VITE_APP_ICP_NO;
|
||||
|
||||
init() {
|
||||
// @ts-ignore
|
||||
_.forEach(import.meta.env, (value, key) => {
|
||||
if (key.startsWith("VITE_APP")) {
|
||||
key = key.replace("VITE_APP_", "");
|
||||
// @ts-ignore
|
||||
this[key] = value;
|
||||
init(env: any) {
|
||||
for (const key in this) {
|
||||
if (this.hasOwnProperty(key)) {
|
||||
this[key] = env[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
// @ts-ignore
|
||||
this.MODE = import.meta.env.MODE;
|
||||
}
|
||||
|
||||
get(key: string, defaultValue: string) {
|
||||
|
|
|
@ -1,55 +1,55 @@
|
|||
import { request } from "/src/api/service";
|
||||
const apiPrefix = "/pi/access";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
export async function GetList(query: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
export async function AddObj(obj: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
export async function UpdateObj(obj: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: number) {
|
||||
return request({
|
||||
export async function DelObj(id: number) {
|
||||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: number) {
|
||||
return request({
|
||||
export async function GetObj(id: number) {
|
||||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetProviderDefine(type: string) {
|
||||
return request({
|
||||
export async function GetProviderDefine(type: string) {
|
||||
return await request({
|
||||
url: apiPrefix + "/define",
|
||||
method: "post",
|
||||
params: { type }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetProviderDefineByAccessType(type: string) {
|
||||
return request({
|
||||
export async function GetProviderDefineByAccessType(type: string) {
|
||||
return await request({
|
||||
url: apiPrefix + "/defineByAccessType",
|
||||
method: "post",
|
||||
params: { type }
|
||||
|
|
|
@ -3,7 +3,8 @@ import { ColumnCompositionProps, dict, compute } from "@fast-crud/fast-crud";
|
|||
import * as api from "./api";
|
||||
// @ts-ignore
|
||||
import _ from "lodash-es";
|
||||
import { toRef } from "vue";
|
||||
import { computed, ref, toRef } from "vue";
|
||||
import { useReference } from "/@/use/use-refrence";
|
||||
|
||||
export function getCommonColumnDefine(crudExpose: any, typeRef: any) {
|
||||
const AccessTypeDictRef = dict({
|
||||
|
@ -32,19 +33,10 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any) {
|
|||
...value,
|
||||
key
|
||||
};
|
||||
let column = _.merge({ title: key }, defaultPluginConfig, field);
|
||||
const column = _.merge({ title: key }, defaultPluginConfig, field);
|
||||
|
||||
//eval
|
||||
if (column.mergeScript) {
|
||||
const ctx = {
|
||||
compute
|
||||
};
|
||||
const script = column.mergeScript;
|
||||
delete column.mergeScript;
|
||||
const func = new Function("ctx", script);
|
||||
const merged = func(ctx);
|
||||
column = _.merge(column, merged);
|
||||
}
|
||||
useReference(column);
|
||||
|
||||
//设置默认值
|
||||
if (column.value != null && _.get(form, key) == null) {
|
||||
|
@ -56,6 +48,8 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any) {
|
|||
});
|
||||
}
|
||||
|
||||
const currentDefine = ref();
|
||||
|
||||
return {
|
||||
type: {
|
||||
title: "类型",
|
||||
|
@ -84,13 +78,21 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any) {
|
|||
return;
|
||||
}
|
||||
const define = await api.GetProviderDefine(value);
|
||||
currentDefine.value = define;
|
||||
console.log("define", define);
|
||||
if (!immediate) {
|
||||
form.access = {};
|
||||
}
|
||||
buildDefineFields(define, form);
|
||||
}
|
||||
},
|
||||
helper: computed(() => {
|
||||
const define = currentDefine.value;
|
||||
if (define == null) {
|
||||
return "";
|
||||
}
|
||||
return define.desc;
|
||||
})
|
||||
},
|
||||
addForm: {
|
||||
value: typeRef
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
<template>
|
||||
<div class="cd-page-account">
|
||||
<iframe >
|
||||
|
||||
</iframe>
|
||||
|
||||
</div>
|
||||
</template>
|
|
@ -2,56 +2,56 @@ import { request } from "/src/api/service";
|
|||
|
||||
const apiPrefix = "/pi/history";
|
||||
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
export async function GetList(query: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
export async function AddObj(obj: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
export async function UpdateObj(obj: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
export async function DelObj(id: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
export async function GetObj(id: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetDetail(id: any) {
|
||||
return request({
|
||||
export async function GetDetail(id: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/detail",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function DeleteBatch(ids: any[]) {
|
||||
return request({
|
||||
export async function DeleteBatch(ids: any[]) {
|
||||
return await request({
|
||||
url: apiPrefix + "/deleteByIds",
|
||||
method: "post",
|
||||
data: { ids }
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { request } from "/src/api/service";
|
||||
|
||||
export async function getMineInfo() {
|
||||
return request({
|
||||
return await request({
|
||||
url: "/mine/info",
|
||||
method: "POST"
|
||||
});
|
||||
}
|
||||
|
||||
export async function changePassword(form: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: "/mine/changePassword",
|
||||
method: "POST",
|
||||
data: form
|
||||
|
|
|
@ -3,72 +3,72 @@ import { request } from "/src/api/service";
|
|||
const apiPrefix = "/pi/pipeline";
|
||||
const historyApiPrefix = "/pi/history";
|
||||
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
export async function GetList(query: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
export async function AddObj(obj: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
export async function UpdateObj(obj: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
export async function DelObj(id: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
export async function GetObj(id: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetDetail(id: any) {
|
||||
return request({
|
||||
export async function GetDetail(id: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/detail",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function Save(pipelineEntity: any) {
|
||||
return request({
|
||||
export async function Save(pipelineEntity: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/save",
|
||||
method: "post",
|
||||
data: pipelineEntity
|
||||
});
|
||||
}
|
||||
|
||||
export function Trigger(id: any, stepId?: string) {
|
||||
return request({
|
||||
export async function Trigger(id: any, stepId?: string) {
|
||||
return await request({
|
||||
url: apiPrefix + "/trigger",
|
||||
method: "post",
|
||||
params: { id, stepId }
|
||||
});
|
||||
}
|
||||
|
||||
export function Cancel(historyId: any) {
|
||||
return request({
|
||||
export async function Cancel(historyId: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/cancel",
|
||||
method: "post",
|
||||
params: { historyId }
|
||||
|
@ -76,7 +76,7 @@ export function Cancel(historyId: any) {
|
|||
}
|
||||
|
||||
export async function GetFiles(pipelineId: number) {
|
||||
return request({
|
||||
return await request({
|
||||
url: historyApiPrefix + "/files",
|
||||
method: "post",
|
||||
params: { pipelineId }
|
||||
|
|
|
@ -84,7 +84,8 @@ export default function (certPluginGroup: PluginGroup, formWrapperRef: any): Cre
|
|||
vModel: "modelValue",
|
||||
placeholder: "0 0 4 * * *"
|
||||
},
|
||||
helper: "点击上面的按钮,选择每天几点几分定时执行, 例如:0 0 4 * * *,每天凌晨4点0分0秒触发\n建议设置为每天触发一次,证书未到期之前任务会跳过,不会重复执行",
|
||||
helper:
|
||||
"点击上面的按钮,选择每天几点几分定时执行,后面的分秒都要选择0。\n例如:0 0 4 * * *,每天凌晨4点0分0秒触发\n建议设置为每天触发一次,证书未到期之前任务会跳过,不会重复执行",
|
||||
order: 100
|
||||
}
|
||||
},
|
||||
|
|
|
@ -113,6 +113,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
|||
stages: [
|
||||
{
|
||||
title: "证书申请阶段",
|
||||
maxTaskCount: 1,
|
||||
tasks: [
|
||||
{
|
||||
title: "证书申请任务",
|
||||
|
|
|
@ -8,10 +8,12 @@
|
|||
@after-open-change="notificationDrawerOnAfterVisibleChange"
|
||||
>
|
||||
<template #title>
|
||||
<div>
|
||||
编辑通知
|
||||
<a-button v-if="mode === 'edit'" @click="notificationDelete()">
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="currentNotification">
|
||||
<pi-container>
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
component: {
|
||||
name: 'a-select',
|
||||
vModel: 'value',
|
||||
mode: 'tags'
|
||||
mode: 'tags',
|
||||
open: false
|
||||
},
|
||||
helper: '输入你的收件邮箱地址,支持多个邮箱',
|
||||
rules: [{ required: true, message: '此项必填' }]
|
||||
}"
|
||||
/>
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
vModel: 'modelValue'
|
||||
},
|
||||
helper:
|
||||
'点击上面的按钮,选择每天几点几分定时执行, 例如:0 0 4 * * *,每天凌晨4点0分0秒触发\n建议设置为每天触发一次,证书未到期之前任务会跳过,不会重复执行',
|
||||
'点击上面的按钮,选择每天几点几分定时执行,后面的分秒都要选择0。\n 例如:0 0 4 * * *,每天凌晨4点0分0秒触发\n建议设置为每天触发一次,证书未到期之前任务会跳过,不会重复执行',
|
||||
rules: [{ required: true, message: '此项必填' }]
|
||||
}"
|
||||
/>
|
||||
|
|
|
@ -123,7 +123,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div v-if="editMode" class="task-container is-add">
|
||||
<div v-if="editMode && !(stage.maxTaskCount > 0 && stage.tasks.length >= stage.maxTaskCount)" class="task-container is-add">
|
||||
<div class="line line-left">
|
||||
<div class="flow-line"></div>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="d2-page-cover">
|
||||
<div class="d2-page-cover__title">
|
||||
<div class="title-line">
|
||||
<img width="50" :src="envRef.LOGO_PATH" />
|
||||
<img width="50" :src="envRef.LOGO" />
|
||||
{{ envRef.TITLE }} v{{ version }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="main">
|
||||
<div class="main login-page">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
class="user-layout-login"
|
||||
|
@ -207,7 +207,10 @@ export default defineComponent({
|
|||
|
||||
<style lang="less">
|
||||
@import "../../../style/theme/index.less";
|
||||
.user-layout-login {
|
||||
.login-page.main {
|
||||
//margin: 20px !important;
|
||||
margin-bottom: 100px;
|
||||
.user-layout-login {
|
||||
label {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
@ -261,5 +264,6 @@ export default defineComponent({
|
|||
.iconify {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import { request } from "/@/api/service";
|
||||
|
||||
export async function PreBindUser(userId: number) {
|
||||
await request({
|
||||
url: "/sys/account/preBindUser",
|
||||
method: "post",
|
||||
data: { userId }
|
||||
});
|
||||
}
|
||||
|
||||
export async function BindUser(userId: number) {
|
||||
await request({
|
||||
url: "/sys/account/bindUser",
|
||||
method: "post",
|
||||
data: { userId }
|
||||
});
|
||||
}
|
||||
|
||||
export async function UnbindUser(userId: number) {
|
||||
await request({
|
||||
url: "/sys/account/unbindUser",
|
||||
method: "post",
|
||||
data: { userId }
|
||||
});
|
||||
}
|
||||
|
||||
export async function UpdateLicense(data: any) {
|
||||
await request({
|
||||
url: "/sys/account/updateLicense",
|
||||
method: "post",
|
||||
data
|
||||
});
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
<template>
|
||||
<fs-page class="cd-page-account">
|
||||
<template #header>
|
||||
<div class="title">
|
||||
站点绑定
|
||||
<span class="sub">管理你安装过的Certd站点,可以通过转移功能避免丢失VIP,强烈建议绑定</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<iframe ref="iframeRef" class="account-iframe" :src="iframeSrcRef"> </iframe>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { IframeClient } from "@certd/lib-iframe";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import * as api from "./api";
|
||||
import { notification } from "ant-design-vue";
|
||||
const iframeRef = ref();
|
||||
|
||||
const userStore = useUserStore();
|
||||
const settingStore = useSettingStore();
|
||||
|
||||
const iframeSrcRef = computed(() => {
|
||||
if (!settingStore.installInfo.accountServerBaseUrl) {
|
||||
return "";
|
||||
}
|
||||
return `${settingStore.installInfo.accountServerBaseUrl}/#/?appKey=${settingStore.installInfo.appKey}`;
|
||||
});
|
||||
|
||||
type SubjectInfo = {
|
||||
subjectId: string;
|
||||
installTime?: number;
|
||||
vipType?: string;
|
||||
expiresTime?: number;
|
||||
};
|
||||
onMounted(() => {
|
||||
const iframeClient = new IframeClient(iframeRef.value, (e: any) => {
|
||||
notification.error({
|
||||
message: " error",
|
||||
description: e.message
|
||||
});
|
||||
});
|
||||
iframeClient.register("getSubjectInfo", async (req) => {
|
||||
const subjectInfo: SubjectInfo = {
|
||||
subjectId: settingStore.installInfo.siteId,
|
||||
installTime: settingStore.installInfo.installTime,
|
||||
vipType: userStore.plusInfo.vipType || "free",
|
||||
expiresTime: userStore.plusInfo.expireTime
|
||||
};
|
||||
return subjectInfo;
|
||||
});
|
||||
|
||||
let preBindUserId = null;
|
||||
iframeClient.register("preBindUser", async (req) => {
|
||||
const userId = req.data.userId;
|
||||
preBindUserId = userId;
|
||||
await api.PreBindUser(userId);
|
||||
});
|
||||
|
||||
iframeClient.register("onBoundUser", async (req) => {
|
||||
await api.BindUser(preBindUserId);
|
||||
});
|
||||
|
||||
iframeClient.register("unbindUser", async (req) => {
|
||||
const userId = req.data.userId;
|
||||
await api.UnbindUser(userId);
|
||||
});
|
||||
|
||||
iframeClient.register("updateLicense", async (req) => {
|
||||
await api.UpdateLicense(req.data);
|
||||
await userStore.reInit();
|
||||
notification.success({
|
||||
message: "更新成功",
|
||||
description: "专业版已激活"
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.cd-page-account {
|
||||
.fs-page-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.account-iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,7 +1,7 @@
|
|||
import { request } from "/src/api/service";
|
||||
const apiPrefix = "/sys/authority/permission";
|
||||
export async function GetList(query: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query
|
||||
|
@ -9,14 +9,14 @@ export async function GetList(query: any) {
|
|||
}
|
||||
|
||||
export async function GetTree() {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/tree",
|
||||
method: "post"
|
||||
});
|
||||
}
|
||||
|
||||
export async function AddObj(obj: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
|
@ -24,7 +24,7 @@ export async function AddObj(obj: any) {
|
|||
}
|
||||
|
||||
export async function UpdateObj(obj: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
|
@ -32,7 +32,7 @@ export async function UpdateObj(obj: any) {
|
|||
}
|
||||
|
||||
export async function DelObj(id: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
|
@ -40,7 +40,7 @@ export async function DelObj(id: any) {
|
|||
}
|
||||
|
||||
export async function GetObj(id: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { request } from "/src/api/service";
|
||||
const apiPrefix = "/sys/authority/role";
|
||||
export async function GetList(query: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query
|
||||
|
@ -9,7 +9,7 @@ export async function GetList(query: any) {
|
|||
}
|
||||
|
||||
export async function AddObj(obj: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
|
@ -17,7 +17,7 @@ export async function AddObj(obj: any) {
|
|||
}
|
||||
|
||||
export async function UpdateObj(obj: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
|
@ -25,7 +25,7 @@ export async function UpdateObj(obj: any) {
|
|||
}
|
||||
|
||||
export async function DelObj(id: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
|
@ -33,7 +33,7 @@ export async function DelObj(id: any) {
|
|||
}
|
||||
|
||||
export async function GetObj(id: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id }
|
||||
|
@ -46,8 +46,8 @@ export async function GetObj(id: any) {
|
|||
* @returns {*}
|
||||
* @constructor
|
||||
*/
|
||||
export function getPermissionIds(roleId: any) {
|
||||
return request({
|
||||
export async function getPermissionIds(roleId: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/getPermissionIds",
|
||||
method: "post",
|
||||
params: { id: roleId }
|
||||
|
@ -61,8 +61,8 @@ export function getPermissionIds(roleId: any) {
|
|||
* @returns {*}
|
||||
* @constructor
|
||||
*/
|
||||
export function DoAuthz(roleId: any, permissionIds: any) {
|
||||
return request({
|
||||
export async function DoAuthz(roleId: any, permissionIds: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/authz",
|
||||
method: "post",
|
||||
data: { roleId, permissionIds }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { request } from "/src/api/service";
|
||||
const apiPrefix = "/sys/authority/user";
|
||||
export async function GetList(query: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query
|
||||
|
@ -9,7 +9,7 @@ export async function GetList(query: any) {
|
|||
}
|
||||
|
||||
export async function AddObj(obj: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
|
@ -17,7 +17,7 @@ export async function AddObj(obj: any) {
|
|||
}
|
||||
|
||||
export async function UpdateObj(obj: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
|
@ -25,7 +25,7 @@ export async function UpdateObj(obj: any) {
|
|||
}
|
||||
|
||||
export async function DelObj(id: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
|
@ -33,7 +33,7 @@ export async function DelObj(id: any) {
|
|||
}
|
||||
|
||||
export async function GetObj(id: any) {
|
||||
return request({
|
||||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id }
|
||||
|
|
|
@ -46,10 +46,12 @@ import { useSettingStore } from "/@/store/modules/settings";
|
|||
|
||||
interface FormState {
|
||||
registerEnabled: boolean;
|
||||
managerOtherUserPipeline: boolean;
|
||||
}
|
||||
|
||||
const formState = reactive<Partial<FormState>>({
|
||||
registerEnabled: false
|
||||
registerEnabled: false,
|
||||
managerOtherUserPipeline: false
|
||||
});
|
||||
|
||||
async function loadSysPublicSettings() {
|
||||
|
@ -61,7 +63,6 @@ async function loadSysPublicSettings() {
|
|||
loadSysPublicSettings();
|
||||
const settingsStore = useSettingStore();
|
||||
const onFinish = async (form: any) => {
|
||||
console.log("Success:", form);
|
||||
await api.PublicSettingsSave(form);
|
||||
await settingsStore.loadSysSettings();
|
||||
notification.success({
|
||||
|
|
|
@ -96,6 +96,7 @@ export default ({ command, mode }) => {
|
|||
}
|
||||
},
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
port: 3002,
|
||||
fs: devServerFs,
|
||||
proxy: {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
koa:
|
||||
port: 7001
|
||||
# key: ./data/ssl/cert.key
|
||||
# cert: ./data/ssl/cert.crt
|
||||
#plus:
|
||||
|
@ -8,3 +6,14 @@ koa:
|
|||
plus:
|
||||
server:
|
||||
baseUrl: 'https://api.ai.handsfree.work'
|
||||
#typeorm:
|
||||
# dataSource:
|
||||
# default:
|
||||
# database: './data/db1.sqlite'
|
||||
#account:
|
||||
# server:
|
||||
# baseUrl: 'http://127.0.0.1:1017/subject'
|
||||
|
||||
account:
|
||||
server:
|
||||
baseUrl: 'https://ai.handsfree.work/subject'
|
||||
|
|
|
@ -10,3 +10,7 @@ typeorm:
|
|||
plus:
|
||||
server:
|
||||
baseUrl: 'https://api.ai.handsfree.work'
|
||||
|
||||
account:
|
||||
server:
|
||||
baseUrl: 'https://ai.handsfree.work/subject'
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
"up-mw-deps": "npx midway-version -u -w",
|
||||
"heap": "clinic heapprofiler -- node ./bootstrap.js",
|
||||
"flame": "clinic flame -- node ./bootstrap.js"
|
||||
|
||||
},
|
||||
"dependencies": {
|
||||
"@alicloud/cs20151215": "^3.0.3",
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@certd/acme-client": "^1.24.4",
|
||||
"@certd/lib-huawei": "^1.24.3",
|
||||
"@certd/lib-jdcloud": "^1.24.4",
|
||||
"@certd/lib-k8s": "^1.24.4",
|
||||
"@certd/midway-flyway-js": "^1.24.4",
|
||||
"@certd/pipeline": "^1.24.4",
|
||||
|
@ -65,6 +65,7 @@
|
|||
"nanoid": "^4.0.0",
|
||||
"nodemailer": "^6.9.3",
|
||||
"pg": "^8.12.0",
|
||||
"qiniu": "^7.12.0",
|
||||
"querystring": "^0.2.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"ssh2": "^1.15.0",
|
||||
|
@ -72,7 +73,8 @@
|
|||
"svg-captcha": "^1.4.0",
|
||||
"syno": "^2.2.0",
|
||||
"tencentcloud-sdk-nodejs": "^4.0.44",
|
||||
"typeorm": "^0.3.20"
|
||||
"typeorm": "^0.3.20",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@midwayjs/mock": "^3.16.4",
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
|
||||
import { BaseController } from '../../basic/base-controller.js';
|
||||
import { PlusService } from '../basic/service/plus-service.js';
|
||||
import { AppKey } from '@certd/pipeline';
|
||||
import { SysSettingsService } from '../system/service/sys-settings-service.js';
|
||||
import { SysInstallInfo } from '../system/service/models.js';
|
||||
|
||||
export type PreBindUserReq = {
|
||||
userId: number;
|
||||
};
|
||||
export type BindUserReq = {
|
||||
userId: number;
|
||||
};
|
||||
/**
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/sys/account')
|
||||
export class BasicController extends BaseController {
|
||||
@Inject()
|
||||
plusService: PlusService;
|
||||
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@Post('/preBindUser', { summary: 'sys:settings:edit' })
|
||||
public async preBindUser(@Body(ALL) body: PreBindUserReq) {
|
||||
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
// 设置缓存内容
|
||||
await this.plusService.requestWithoutSign({
|
||||
url: '/activation/subject/preBind',
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId: body.userId,
|
||||
appKey: AppKey,
|
||||
subjectId: installInfo.siteId,
|
||||
},
|
||||
});
|
||||
|
||||
return this.ok({});
|
||||
}
|
||||
|
||||
@Post('/bindUser', { summary: 'sys:settings:edit' })
|
||||
public async bindUser(@Body(ALL) body: BindUserReq) {
|
||||
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
installInfo.bindUserId = body.userId;
|
||||
await this.sysSettingsService.saveSetting(installInfo);
|
||||
return this.ok({});
|
||||
}
|
||||
|
||||
@Post('/unbindUser', { summary: 'sys:settings:edit' })
|
||||
public async unbindUser() {
|
||||
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
installInfo.bindUserId = null;
|
||||
await this.sysSettingsService.saveSetting(installInfo);
|
||||
return this.ok({});
|
||||
}
|
||||
|
||||
@Post('/updateLicense', { summary: 'sys:settings:edit' })
|
||||
public async updateLicense(@Body(ALL) body: { license: string }) {
|
||||
await this.plusService.updateLicense(body.license);
|
||||
return this.ok(true);
|
||||
}
|
||||
}
|
|
@ -3,9 +3,10 @@ import { logger } from '../../utils/logger.js';
|
|||
import { UserService } from '../authority/service/user-service.js';
|
||||
import { SysSettingsService } from '../system/service/sys-settings-service.js';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { SysInstallInfo, SysLicenseInfo, SysPrivateSettings } from '../system/service/models.js';
|
||||
import { verify } from '@certd/pipeline';
|
||||
import { SysInstallInfo, SysPrivateSettings } from '../system/service/models.js';
|
||||
import crypto from 'crypto';
|
||||
import { PlusService } from '../basic/service/plus-service.js';
|
||||
|
||||
export type InstallInfo = {
|
||||
installTime: number;
|
||||
instanceId?: string;
|
||||
|
@ -22,6 +23,8 @@ export class AutoInitSite {
|
|||
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
@Inject()
|
||||
plusService: PlusService;
|
||||
|
||||
@Init()
|
||||
async init() {
|
||||
|
@ -52,12 +55,7 @@ export class AutoInitSite {
|
|||
}
|
||||
|
||||
// 授权许可
|
||||
const licenseInfo: SysLicenseInfo = await this.sysSettingsService.getSetting(SysLicenseInfo);
|
||||
const req = {
|
||||
subjectId: installInfo.siteId,
|
||||
license: licenseInfo.license,
|
||||
};
|
||||
await verify(req);
|
||||
await this.plusService.verify();
|
||||
|
||||
logger.info('初始化站点完成');
|
||||
}
|
||||
|
|
|
@ -1,23 +1,9 @@
|
|||
import { Rule, RuleType } from '@midwayjs/validate';
|
||||
import { Controller, Get, Inject, Provide } from '@midwayjs/core';
|
||||
import { Config, Controller, Get, Inject, Provide } from '@midwayjs/core';
|
||||
import { BaseController } from '../../../basic/base-controller.js';
|
||||
import { Constants } from '../../../basic/constants.js';
|
||||
import { SysSettingsService } from '../../system/service/sys-settings-service.js';
|
||||
import { SysInstallInfo, SysPublicSettings } from '../../system/service/models.js';
|
||||
|
||||
export class SmsCodeReq {
|
||||
@Rule(RuleType.number().required())
|
||||
phoneCode: number;
|
||||
|
||||
@Rule(RuleType.string().required())
|
||||
mobile: string;
|
||||
|
||||
@Rule(RuleType.string().required().max(10))
|
||||
randomStr: string;
|
||||
|
||||
@Rule(RuleType.number().required().max(4))
|
||||
imgCode: string;
|
||||
}
|
||||
import { SysInstallInfo, SysPublicSettings, SysSiteInfo } from '../../system/service/models.js';
|
||||
import { AppKey } from '@certd/pipeline';
|
||||
|
||||
/**
|
||||
*/
|
||||
|
@ -26,6 +12,8 @@ export class SmsCodeReq {
|
|||
export class BasicSettingsController extends BaseController {
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
@Config('account.server.baseUrl')
|
||||
accountServerBaseUrl: any;
|
||||
|
||||
@Get('/public', { summary: Constants.per.guest })
|
||||
public async getSysPublic() {
|
||||
|
@ -35,7 +23,15 @@ export class BasicSettingsController extends BaseController {
|
|||
|
||||
@Get('/install', { summary: Constants.per.guest })
|
||||
public async getInstallInfo() {
|
||||
const settings = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
const settings: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
settings.accountServerBaseUrl = this.accountServerBaseUrl;
|
||||
settings.appKey = AppKey;
|
||||
return this.ok(settings);
|
||||
}
|
||||
|
||||
@Get('/siteInfo', { summary: Constants.per.guest })
|
||||
public async getSiteInfo() {
|
||||
const settings: SysSiteInfo = await this.sysSettingsService.getSetting(SysSiteInfo);
|
||||
return this.ok(settings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { Config, Init, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { SysSettingsService } from '../../system/service/sys-settings-service.js';
|
||||
import { SysInstallInfo } from '../../system/service/models.js';
|
||||
import { AppKey, getPlusInfo, isPlus } from '@certd/pipeline';
|
||||
import * as crypto from 'crypto';
|
||||
import { request } from '../../../utils/http.js';
|
||||
import { SysInstallInfo, SysLicenseInfo } from '../../system/service/models.js';
|
||||
import { AppKey, http, PlusRequestService, verify } from '@certd/pipeline';
|
||||
import { logger } from '../../../utils/logger.js';
|
||||
|
||||
@Provide()
|
||||
|
@ -14,66 +12,69 @@ export class PlusService {
|
|||
@Config('plus.server.baseUrl')
|
||||
plusServerBaseUrl;
|
||||
|
||||
async requestWithoutSign(config: any): Promise<any> {
|
||||
config.baseURL = this.plusServerBaseUrl;
|
||||
return await request(config);
|
||||
}
|
||||
plusRequestService: PlusRequestService;
|
||||
|
||||
async request(config: any) {
|
||||
if (!isPlus()) {
|
||||
throw new Error('您还不是专业版,请先激活专业版');
|
||||
}
|
||||
const { url, data } = config;
|
||||
const timestamps = Date.now();
|
||||
@Init()
|
||||
async init() {
|
||||
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
const sign = await this.sign(data, timestamps);
|
||||
|
||||
const requestHeader = {
|
||||
this.plusRequestService = new PlusRequestService({
|
||||
plusServerBaseUrl: this.plusServerBaseUrl,
|
||||
http: http,
|
||||
logger,
|
||||
subjectId: installInfo.siteId,
|
||||
appKey: AppKey,
|
||||
sign: sign,
|
||||
timestamps: timestamps,
|
||||
};
|
||||
let requestHeaderStr = JSON.stringify(requestHeader);
|
||||
requestHeaderStr = Buffer.from(requestHeaderStr).toString('base64');
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Plus-Subject': requestHeaderStr,
|
||||
};
|
||||
return await request({
|
||||
url: url,
|
||||
baseURL: this.plusServerBaseUrl,
|
||||
method: 'POST',
|
||||
data: data,
|
||||
headers: headers,
|
||||
});
|
||||
}
|
||||
|
||||
async sign(body: any, timestamps: number) {
|
||||
//content := fmt.Sprintf("%s.%d.%s", in.Params, in.Timestamps, secret)
|
||||
const params = JSON.stringify(body);
|
||||
const plusInfo = getPlusInfo();
|
||||
const secret = plusInfo.secret;
|
||||
if (!secret) {
|
||||
const randomTime = Math.floor(Math.random() * 3 * 60 * 1000 + 30 * 1000);
|
||||
setTimeout(() => {
|
||||
process.exit();
|
||||
}, randomTime);
|
||||
return 'xxxxx';
|
||||
async requestWithoutSign(config: any) {
|
||||
return await this.plusRequestService.requestWithoutSign(config);
|
||||
}
|
||||
const content = `${params}.${timestamps}.${secret}`;
|
||||
|
||||
// sha256
|
||||
const sign = crypto.createHash('sha256').update(content).digest('base64');
|
||||
logger.info('content:', content, 'sign:', sign);
|
||||
return sign;
|
||||
async request(config: any) {
|
||||
return await this.plusRequestService.request(config);
|
||||
}
|
||||
|
||||
async active(formData: { code: any; appKey: string; subjectId: string }) {
|
||||
return await this.requestWithoutSign({
|
||||
return await this.plusRequestService.requestWithoutSign({
|
||||
url: '/activation/active',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
});
|
||||
}
|
||||
|
||||
async updateLicense(license: string) {
|
||||
let licenseInfo: SysLicenseInfo = await this.sysSettingsService.getSetting(SysLicenseInfo);
|
||||
if (!licenseInfo) {
|
||||
licenseInfo = new SysLicenseInfo();
|
||||
}
|
||||
licenseInfo.license = license;
|
||||
await this.sysSettingsService.saveSetting(licenseInfo);
|
||||
await this.verify();
|
||||
}
|
||||
async verify() {
|
||||
const licenseInfo: SysLicenseInfo = await this.sysSettingsService.getSetting(SysLicenseInfo);
|
||||
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
|
||||
const verifyRes = await verify({
|
||||
subjectId: installInfo.siteId,
|
||||
license: licenseInfo.license,
|
||||
plusRequestService: this.plusRequestService,
|
||||
bindUrl: installInfo?.bindUrl,
|
||||
});
|
||||
|
||||
if (!verifyRes.isPlus) {
|
||||
const message = verifyRes.message || '授权码校验失败';
|
||||
logger.error(message);
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
async bindUrl(subjectId: string, url: string) {
|
||||
return await this.plusRequestService.request({
|
||||
url: '/activation/subject/urlBind',
|
||||
data: {
|
||||
subjectId,
|
||||
appKey: AppKey,
|
||||
url,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import { ALL, Body, Controller, Post, Provide } from '@midwayjs/core';
|
||||
import { Constants } from '../../../basic/constants.js';
|
||||
import { accessRegistry, http, logger, PluginRequest, RequestHandleContext } from '@certd/pipeline';
|
||||
import { merge } from 'lodash-es';
|
||||
import { BaseController } from '../../../basic/base-controller.js';
|
||||
@Provide()
|
||||
@Controller('/api/pi/handle')
|
||||
export class HandleController extends BaseController {
|
||||
@Post('/', { summary: Constants.per.authOnly })
|
||||
async request(@Body(ALL) body: PluginRequest) {
|
||||
const type = body.type;
|
||||
if (type === 'access') {
|
||||
const accessItem = accessRegistry.get(body.typeName);
|
||||
const accessCls = accessItem.target;
|
||||
if (accessCls == null) {
|
||||
throw new Error(`access ${body.typeName} not found`);
|
||||
}
|
||||
//实例化access
|
||||
//@ts-ignore
|
||||
const access = new accessCls();
|
||||
//注入input
|
||||
merge(access, body.input);
|
||||
const ctx: RequestHandleContext = {
|
||||
http: http,
|
||||
logger: logger,
|
||||
};
|
||||
const res = await access.onRequest(body, ctx);
|
||||
|
||||
return this.ok(res);
|
||||
} else if (type === 'plugin') {
|
||||
throw new Error(`plugin:${body.typeName} not support`);
|
||||
} else {
|
||||
throw new Error(`type:${type} not support`);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
|
||||
import { SysSettingsService } from '../service/sys-settings-service.js';
|
||||
import { BaseController } from '../../../basic/base-controller.js';
|
||||
import { AppKey, verify } from '@certd/pipeline';
|
||||
import { SysInstallInfo, SysLicenseInfo } from '../service/models.js';
|
||||
import { AppKey } from '@certd/pipeline';
|
||||
import { SysInstallInfo } from '../service/models.js';
|
||||
import { logger } from '../../../utils/logger.js';
|
||||
import { PlusService } from '../../basic/service/plus-service.js';
|
||||
|
||||
|
@ -21,10 +21,11 @@ export class SysPlusController extends BaseController {
|
|||
async active(@Body(ALL) body) {
|
||||
const { code } = body;
|
||||
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
const siteId = installInfo.siteId;
|
||||
const formData = {
|
||||
appKey: AppKey,
|
||||
code,
|
||||
subjectId: installInfo.siteId,
|
||||
subjectId: siteId,
|
||||
};
|
||||
|
||||
const res: any = await this.plusService.active(formData);
|
||||
|
@ -33,26 +34,22 @@ export class SysPlusController extends BaseController {
|
|||
logger.error('激活失败', res.message);
|
||||
return this.fail(res.message, 1);
|
||||
}
|
||||
|
||||
const license = res.data.license;
|
||||
|
||||
let licenseInfo: SysLicenseInfo = await this.sysSettingsService.getSetting(SysLicenseInfo);
|
||||
if (!licenseInfo) {
|
||||
licenseInfo = new SysLicenseInfo();
|
||||
}
|
||||
licenseInfo.license = license;
|
||||
await this.sysSettingsService.saveSetting(licenseInfo);
|
||||
await this.plusService.updateLicense(license);
|
||||
|
||||
const verifyRes = await verify({
|
||||
subjectId: installInfo.siteId,
|
||||
license,
|
||||
});
|
||||
|
||||
if (!verifyRes.isPlus) {
|
||||
const message = verifyRes.message || '授权码校验失败';
|
||||
logger.error(message);
|
||||
return this.fail(message, 1);
|
||||
return this.ok(true);
|
||||
}
|
||||
return this.ok(res.data);
|
||||
@Post('/bindUrl', { summary: 'sys:settings:edit' })
|
||||
async bindUrl(@Body(ALL) body: { url: string }) {
|
||||
const { url } = body;
|
||||
|
||||
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
await this.plusService.bindUrl(installInfo.siteId, url);
|
||||
|
||||
installInfo.bindUrl = url;
|
||||
await this.sysSettingsService.saveSetting(installInfo);
|
||||
|
||||
return this.ok(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,10 @@ export class SysInstallInfo extends BaseSettings {
|
|||
static __access__ = 'private';
|
||||
installTime: number;
|
||||
siteId?: string;
|
||||
bindUserId?: number;
|
||||
bindUrl?: string;
|
||||
accountServerBaseUrl?: string;
|
||||
appKey?: string;
|
||||
}
|
||||
|
||||
export class SysLicenseInfo extends BaseSettings {
|
||||
|
@ -38,3 +42,13 @@ export class SysLicenseInfo extends BaseSettings {
|
|||
static __access__ = 'private';
|
||||
license?: string;
|
||||
}
|
||||
|
||||
export class SysSiteInfo extends BaseSettings {
|
||||
static __title__ = '站点信息';
|
||||
static __key__ = 'sys.site';
|
||||
static __access__ = 'public';
|
||||
TITLE?: string;
|
||||
SLOGAN?: string;
|
||||
LOGO?: string;
|
||||
ICP_NO?: string;
|
||||
}
|
||||
|
|
|
@ -8,3 +8,5 @@ export * from './plugin-demo/index.js';
|
|||
export * from './plugin-other/index.js';
|
||||
export * from './plugin-west/index.js';
|
||||
export * from './plugin-doge/index.js';
|
||||
export * from './plugin-qiniu/index.js';
|
||||
export * from './plugin-jdcloud/index.js';
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import { AccessInput, IsAccess } from '@certd/pipeline';
|
||||
|
||||
/**
|
||||
* 这个注解将注册一个授权配置
|
||||
* 在certd的后台管理系统中,用户可以选择添加此类型的授权
|
||||
*/
|
||||
@IsAccess({
|
||||
name: 'dynadot',
|
||||
title: 'dynadot授权',
|
||||
desc: '************\n注意:申请证书时会覆盖已有的域名解析配置,慎用\n************\n待优化,主要是dynadot的接口一言难尽',
|
||||
})
|
||||
export class DynadotAccess {
|
||||
/**
|
||||
* 授权属性配置
|
||||
*/
|
||||
@AccessInput({
|
||||
title: 'API Production Key',
|
||||
component: {
|
||||
placeholder: '授权key',
|
||||
},
|
||||
helper: '前往 [Dynadot API](https://www.dynadot.com/account/domain/setting/api.html) 获取 API Production Key',
|
||||
required: true,
|
||||
encrypt: true,
|
||||
})
|
||||
apiProductionKey = '';
|
||||
}
|
||||
|
||||
new DynadotAccess();
|
|
@ -0,0 +1,93 @@
|
|||
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
|
||||
import { Autowire, ILogger } from '@certd/pipeline';
|
||||
import { DynadotAccess } from './access.js';
|
||||
import querystring from 'querystring';
|
||||
|
||||
// 这里通过IsDnsProvider注册一个dnsProvider
|
||||
@IsDnsProvider({
|
||||
name: 'dynadot',
|
||||
title: 'dynadot',
|
||||
desc: 'dynadot dns provider',
|
||||
// 这里是对应的 cloudflare的access类型名称
|
||||
accessType: 'dynadot',
|
||||
})
|
||||
export class DynadotDnsProvider extends AbstractDnsProvider {
|
||||
// 通过Autowire传递context
|
||||
@Autowire()
|
||||
logger!: ILogger;
|
||||
access!: DynadotAccess;
|
||||
async onInstance() {
|
||||
this.access = this.ctx.access as DynadotAccess;
|
||||
}
|
||||
|
||||
private async doRequest(command: string, query: any) {
|
||||
const baseUrl = 'https://api.dynadot.com/api3.json?key=' + this.access.apiProductionKey;
|
||||
const qs = querystring.stringify(query);
|
||||
const url = `${baseUrl}&command=${command}&${qs}`;
|
||||
const res = await this.ctx.http.request<any, any>({
|
||||
url,
|
||||
method: 'get',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
/*
|
||||
"SetDnsResponse": {
|
||||
"ResponseCode": 0,
|
||||
"Status": "success"
|
||||
}
|
||||
*/
|
||||
for (const resKey in res) {
|
||||
if (res[resKey].ResponseCode != null && res[resKey].ResponseCode !== 0) {
|
||||
throw new Error(`请求失败:${res[resKey].Status}`);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建dns解析记录,用于验证域名所有权
|
||||
*/
|
||||
async createRecord(options: CreateRecordOptions) {
|
||||
/**
|
||||
* fullRecord: '_acme-challenge.test.example.com',
|
||||
* value: 一串uuid
|
||||
* type: 'TXT',
|
||||
* domain: 'example.com'
|
||||
*/
|
||||
const { fullRecord, value, type, domain } = options;
|
||||
this.logger.info('添加域名解析:', fullRecord, value, type, domain);
|
||||
|
||||
//先获取域名原始解析记录
|
||||
//https://api.dynadot.com/api3.xml?key=[API Key]&command=domain_info&domain=domain1.com
|
||||
const res1 = await this.doRequest('domain_info', {
|
||||
domain: domain,
|
||||
});
|
||||
// this.logger.info(`域名信息:${JSON.stringify(res1)}`);
|
||||
// "DomainInfoResponse.NameServerSettings":{"Type":"Dynadot DNS","SubDomains":[{"Subhost":"_acme-challenge","RecordType":"TXT","Value":"43XrhFA6pJpE7a-20y7BmC6CsN20TMt5l-Zl-CL_-4I"}],"TTL":"300"}
|
||||
this.logger.info('原始域名解析记录:', JSON.stringify(res1.DomainInfoResponse?.DomainInfo?.NameServerSettings));
|
||||
const prefix = fullRecord.replace(`.${domain}`, '');
|
||||
// 给domain下创建txt类型的dns解析记录,fullRecord
|
||||
const res = await this.doRequest('set_dns2', {
|
||||
domain: domain,
|
||||
subdomain0: prefix,
|
||||
sub_record_type0: 'TXT',
|
||||
sub_record0: value,
|
||||
});
|
||||
this.logger.info(`添加域名解析成功:fullRecord=${fullRecord},value=${value}`);
|
||||
this.logger.info(`请求结果:${JSON.stringify(res)}`);
|
||||
|
||||
//本接口需要返回本次创建的dns解析记录,这个记录会在删除的时候用到
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除dns解析记录,清理申请痕迹
|
||||
* @param options
|
||||
*/
|
||||
async removeRecord(options: RemoveRecordOptions<any>): Promise<void> {}
|
||||
}
|
||||
|
||||
//实例化这个provider,将其自动注册到系统中
|
||||
new DynadotDnsProvider();
|
|
@ -0,0 +1,3 @@
|
|||
export * from './dns-provider.js';
|
||||
export * from './plugins/index.js';
|
||||
export * from './access.js';
|
|
@ -286,7 +286,16 @@ export class SshClient {
|
|||
async _call(options: { connectConf: SshAccess; callable: any }): Promise<string[]> {
|
||||
const { connectConf, callable } = options;
|
||||
const conn = new AsyncSsh2Client(connectConf, this.logger);
|
||||
try {
|
||||
await conn.connect();
|
||||
} catch (e: any) {
|
||||
if (e.message?.indexOf('All configured authentication methods failed') > -1) {
|
||||
this.logger.error(e);
|
||||
throw new Error('登录失败,请检查用户名/密码/密钥是否正确');
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
try {
|
||||
return await callable(conn);
|
||||
} finally {
|
||||
|
|
|
@ -8,6 +8,7 @@ import path from 'path';
|
|||
name: 'CopyToLocal',
|
||||
title: '复制到本机',
|
||||
icon: 'solar:copy-bold-duotone',
|
||||
desc: '实际上是复制证书到docker容器内的某个路径,需要做目录映射到宿主机',
|
||||
group: pluginGroups.host.key,
|
||||
default: {
|
||||
strategy: {
|
||||
|
@ -18,7 +19,7 @@ import path from 'path';
|
|||
export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
|
||||
@TaskInput({
|
||||
title: '证书保存路径',
|
||||
helper: '需要有写入权限,路径要包含文件名,文件名不能用*?!等特殊符号\n推荐使用相对路径,将写入与数据库同级目录,无需映射,例如:./tmp/cert.pem',
|
||||
helper: '路径要包含文件名,文件名不能用*?!等特殊符号' + '\n推荐使用相对路径,将写入与数据库同级目录,无需映射,例如:./tmp/cert.pem',
|
||||
component: {
|
||||
placeholder: './tmp/cert.pem',
|
||||
},
|
||||
|
@ -26,7 +27,7 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
|
|||
crtPath!: string;
|
||||
@TaskInput({
|
||||
title: '私钥保存路径',
|
||||
helper: '需要有写入权限,路径要包含文件名,文件名不能用*?!等特殊符号\n推荐使用相对路径,将写入与数据库同级目录,无需映射,例如:./tmp/cert.key',
|
||||
helper: '路径要包含文件名,文件名不能用*?!等特殊符号\n推荐使用相对路径,将写入与数据库同级目录,无需映射,例如:./tmp/cert.key',
|
||||
component: {
|
||||
placeholder: './tmp/cert.key',
|
||||
},
|
||||
|
@ -35,7 +36,7 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
|
|||
|
||||
@TaskInput({
|
||||
title: 'PFX证书保存路径',
|
||||
helper: '需要有写入权限,路径要包含文件名,文件名不能用*?!等特殊符号\n推荐使用相对路径,将写入与数据库同级目录,无需映射,例如:./tmp/cert.pfx',
|
||||
helper: '用于IIS证书部署,路径要包含文件名,文件名不能用*?!等特殊符号\n推荐使用相对路径,将写入与数据库同级目录,无需映射,例如:./tmp/cert.pfx',
|
||||
component: {
|
||||
placeholder: './tmp/cert.pfx',
|
||||
},
|
||||
|
@ -45,7 +46,7 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
|
|||
@TaskInput({
|
||||
title: 'DER证书保存路径',
|
||||
helper:
|
||||
'需要有写入权限,路径要包含文件名,文件名不能用*?!等特殊符号\n推荐使用相对路径,将写入与数据库同级目录,无需映射,例如:./tmp/cert.der\n.der和.cer是相同的东西,改个后缀名即可',
|
||||
'用户Apache证书部署,路径要包含文件名,文件名不能用*?!等特殊符号\n推荐使用相对路径,将写入与数据库同级目录,无需映射,例如:./tmp/cert.der\n.der和.cer是相同的东西,改个后缀名即可',
|
||||
component: {
|
||||
placeholder: './tmp/cert.der 或 ./tmp/cert.cer',
|
||||
},
|
||||
|
@ -124,7 +125,9 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
|
|||
this.hostDerPath = derPath;
|
||||
}
|
||||
this.logger.info('请注意,如果使用的是相对路径,那么文件就在你的数据库同级目录下,默认是/data/certd/下面');
|
||||
this.logger.info('请注意,如果使用的是绝对路径,文件在容器内的目录下,你需要给容器做目录映射才能复制到宿主机');
|
||||
this.logger.info(
|
||||
'请注意,如果使用的是绝对路径,文件在容器内的目录下,你需要给容器做目录映射才能复制到宿主机,需要在docker-compose.yaml中配置主机目录映射: volumes: /你宿主机的路径:/任务配置的证书路径'
|
||||
);
|
||||
};
|
||||
|
||||
await certReader.readCertFile({ logger: this.logger, handle });
|
||||
|
|
|
@ -4,7 +4,7 @@ import { SshClient } from '../../lib/ssh.js';
|
|||
@IsTaskPlugin({
|
||||
name: 'hostShellExecute',
|
||||
title: '执行远程主机脚本命令',
|
||||
icon:"tabler:brand-powershell",
|
||||
icon: 'tabler:brand-powershell',
|
||||
group: pluginGroups.host.key,
|
||||
input: {},
|
||||
default: {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue