重构了config的结构和相关代码

This commit is contained in:
Xu Chang 2022-09-28 15:46:29 +08:00
parent 963bfd9a77
commit 2b1fb285b5
47 changed files with 830 additions and 849 deletions

View File

@ -3,7 +3,7 @@ import { AppType } from '../general-app-domain/Application/Schema';
import { EntityDict } from "../general-app-domain";
import { QiniuUploadInfo } from "oak-frontend-base/lib/types/Upload";
import { RuntimeContext } from '../context/RuntimeContext';
import { Schema as Livestream } from '../general-app-domain/Livestream/Schema';
import { Origin } from "../types/Config";
declare type GeneralAspectDict<ED extends EntityDict, Cxt extends RuntimeContext<ED>> = {
loginByMobile: (params: {
captcha?: string;
@ -27,22 +27,10 @@ declare type GeneralAspectDict<ED extends EntityDict, Cxt extends RuntimeContext
signature: string;
}, context: Cxt) => Promise<void>;
getUploadInfo: (params: {
origin: string;
origin: Origin;
bucket?: string;
key?: string;
}, context: Cxt) => Promise<QiniuUploadInfo>;
getLivestream: (params: {
streamTitle: string;
expireAt: number;
}, context: Cxt) => Promise<Pick<Livestream, 'streamTitle' | 'hub' | 'rtmpPushUrl' | 'rtmpPlayUrl' | 'pcPushUrl' | 'streamKey' | 'expireAt'>>;
getLivestream2: (params: {
streamTitle: string;
expireAt: number;
}, context: Cxt) => Promise<Pick<Livestream, 'streamTitle' | 'hub' | 'rtmpPushUrl' | 'rtmpPlayUrl' | 'pcPushUrl' | 'streamKey' | 'expireAt'>>;
getPlayBackUrl: (params: {
streamTitle: string;
start: number;
end: number;
}, context: Cxt) => Promise<string>;
sendCaptcha: (params: {
mobile: string;
env: WechatMpEnv | WebEnv;

View File

@ -1,7 +1,9 @@
import { RuntimeContext } from '../context/RuntimeContext';
import { EntityDict } from '../general-app-domain';
import { Origin } from '../types/Config';
import { QiniuUploadInfo } from 'oak-frontend-base/lib/types/Upload';
export declare function getUploadInfo<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(params: {
origin: string;
origin: Origin;
bucket?: string;
key?: string;
}, context: Cxt): Promise<QiniuUploadInfo>;

View File

@ -2,46 +2,21 @@
Object.defineProperty(exports, "__esModule", { value: true });
exports.getUploadInfo = void 0;
var tslib_1 = require("tslib");
var qiniu_1 = tslib_1.__importDefault(require("../utils/externalUpload/qiniu"));
var ExternalUploadClazz = {
qiniu: qiniu_1.default,
};
var getContextConfig_1 = require("../utils/getContextConfig");
var console_1 = require("console");
function getUploadInfo(params, context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var rowStore, application, _a, type, config, systemId, origin, key, _b, system, systemConfig, originConfig, instance, uploadInfo;
var origin, key, bucket, _a, instance, config, _b, uploadHost, domain, bucket2;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
rowStore = context.rowStore;
return [4 /*yield*/, context.getApplication()];
origin = params.origin, key = params.key, bucket = params.bucket;
return [4 /*yield*/, (0, getContextConfig_1.getConfig)(context, 'Cos', origin)];
case 1:
application = _c.sent();
_a = application, type = _a.type, config = _a.config, systemId = _a.systemId;
origin = params.origin, key = params.key;
return [4 /*yield*/, rowStore.select('system', {
data: {
id: 1,
config: 1
},
filter: {
id: systemId
}
}, context, {
dontCollect: true,
})];
case 2:
_b = tslib_1.__read.apply(void 0, [(_c.sent()).result, 1]), system = _b[0];
try {
systemConfig = system.config;
originConfig = systemConfig.Cos[origin];
instance = new ExternalUploadClazz[origin](originConfig);
uploadInfo = instance.getUploadInfo(key);
return [2 /*return*/, uploadInfo];
}
catch (err) {
throw err;
}
return [2 /*return*/];
_a = _c.sent(), instance = _a.instance, config = _a.config;
(0, console_1.assert)(origin === 'qiniu');
_b = config, uploadHost = _b.uploadHost, domain = _b.domain, bucket2 = _b.bucket;
return [2 /*return*/, instance.getUploadInfo(uploadHost, domain, bucket || bucket2, key)];
}
});
});

View File

@ -1,6 +1,5 @@
import { loginByMobile, loginWechat, loginWechatMp, syncUserInfoWechatMp, sendCaptcha } from './token';
import { getUploadInfo } from './extraFile';
import { getLivestream, getLivestream2, getPlayBackUrl } from './livestream';
export declare const aspectDict: {
loginByMobile: typeof loginByMobile;
loginWechat: typeof loginWechat;
@ -9,7 +8,4 @@ export declare const aspectDict: {
getUploadInfo: typeof getUploadInfo;
sendCaptcha: typeof sendCaptcha;
getApplication: typeof import("./application.dev").getApplication | typeof import("./application.prod").getApplication;
getLivestream: typeof getLivestream;
getLivestream2: typeof getLivestream2;
getPlayBackUrl: typeof getPlayBackUrl;
};

View File

@ -4,7 +4,6 @@ exports.aspectDict = void 0;
var token_1 = require("./token");
var extraFile_1 = require("./extraFile");
var application_1 = require("./application");
var livestream_1 = require("./livestream");
// import commonAspectDict from 'oak-common-aspect';
exports.aspectDict = {
loginByMobile: token_1.loginByMobile,
@ -14,8 +13,5 @@ exports.aspectDict = {
getUploadInfo: extraFile_1.getUploadInfo,
sendCaptcha: token_1.sendCaptcha,
getApplication: application_1.getApplication,
getLivestream: livestream_1.getLivestream,
getLivestream2: livestream_1.getLivestream2,
getPlayBackUrl: livestream_1.getPlayBackUrl,
};
// export type AspectDict<ED extends EntityDict & BaseEntityDict> = TokenAD<ED> & CrudAD<ED>;

View File

@ -1,241 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPlayBackUrl = exports.getLivestream2 = exports.getLivestream = void 0;
var tslib_1 = require("tslib");
var qiniu_live_1 = tslib_1.__importDefault(require("../utils/externalUpload/qiniu_live"));
var sign_1 = require("../utils/sign");
var ts_md5_1 = require("ts-md5");
function getQiniuUploadInfo(context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var rowStore, application, _a, type, config, systemId, origin, _b, system, systemConfig, originConfig;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
rowStore = context.rowStore;
return [4 /*yield*/, context.getApplication()];
case 1:
application = _c.sent();
_a = application, type = _a.type, config = _a.config, systemId = _a.systemId;
origin = "qiniu";
return [4 /*yield*/, rowStore.select('system', {
data: {
id: 1,
config: 1
},
filter: {
id: systemId
}
}, context, {
dontCollect: true,
})];
case 2:
_b = tslib_1.__read.apply(void 0, [(_c.sent()).result, 1]), system = _b[0];
try {
systemConfig = system.config;
originConfig = systemConfig.Cos[origin];
return [2 /*return*/, originConfig];
}
catch (err) {
throw err;
}
return [2 /*return*/];
}
});
});
}
function getQiniuToken(config, params) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var method, path, rawQuery, contentType, bodyStr, liveHost, accessKey, secretKey, instance, token;
return tslib_1.__generator(this, function (_a) {
method = params.method, path = params.path, rawQuery = params.rawQuery, contentType = params.contentType, bodyStr = params.bodyStr;
liveHost = config.liveHost, accessKey = config.accessKey, secretKey = config.secretKey;
// 请求鉴权
try {
instance = new qiniu_live_1.default({
accessKey: accessKey,
secretKey: secretKey,
host: liveHost,
method: method,
path: path,
rawQuery: rawQuery,
contentType: contentType,
bodyStr: bodyStr,
});
token = instance.getToken();
// 拿到鉴权Token
return [2 /*return*/, {
token: token,
}];
}
catch (err) {
throw err;
}
return [2 /*return*/];
});
});
}
/**
* 创建直播流并生成推拉流地址
* @param streamTitle 直播流名称
* @param expireAt 推流过期时间
* @param context context
* @returns Livestream 对象
*/
function getLivestream(params, context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var streamTitle, expireAt, config, hub, path, key, bodyStr, contentType, token, url, obj;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
streamTitle = params.streamTitle, expireAt = params.expireAt;
return [4 /*yield*/, getQiniuUploadInfo(context)];
case 1:
config = _a.sent();
hub = config.hub;
path = "/v2/hubs/".concat(hub, "/streams");
key = streamTitle;
if (!key) {
key = "class".concat(new Date().getTime());
}
bodyStr = JSON.stringify({
key: key,
});
contentType = 'application/json';
return [4 /*yield*/, getQiniuToken(config, {
method: 'POST',
path: path,
contentType: contentType,
bodyStr: bodyStr,
})];
case 2:
token = (_a.sent()).token;
url = "https://pili.qiniuapi.com/v2/hubs/".concat(hub, "/streams");
fetch(url, {
method: 'POST',
headers: {
Authorization: token,
'Content-Type': contentType,
},
body: bodyStr,
mode: 'no-cors',
})
.then(function (res) {
console.log(res.json());
}).then(function (res) {
console.log(res);
}).catch(function (e) {
console.log(e);
});
return [4 /*yield*/, getStreamObj(config, streamTitle, expireAt)];
case 3:
obj = _a.sent();
return [2 /*return*/, obj];
}
});
});
}
exports.getLivestream = getLivestream;
// 获取推拉流地址
/**
* 直播流已存在的情况下获取推拉流地址
* @param streamTitle 直播流名称
* @param expireAt 推流过期时间
* @param context context
* @returns livestream对象
*/
function getLivestream2(params, context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var streamTitle, expireAt, config, livestream;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
streamTitle = params.streamTitle, expireAt = params.expireAt;
return [4 /*yield*/, getQiniuUploadInfo(context)];
case 1:
config = _a.sent();
return [4 /*yield*/, getStreamObj(config, streamTitle, expireAt)];
case 2:
livestream = _a.sent();
return [2 /*return*/, livestream];
}
});
});
}
exports.getLivestream2 = getLivestream2;
function getStreamObj(config, streamTitle, expireAt) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var hub, publishDomain, playDomain, publishKey, playKey, signStr, sourcePath, token, rtmpPushUrl, t, playSign, rtmpPlayUrl, pcPushUrl, streamKey;
return tslib_1.__generator(this, function (_a) {
hub = config.hub, publishDomain = config.publishDomain, playDomain = config.playDomain, publishKey = config.publishKey, playKey = config.playKey;
signStr = "/".concat(hub, "/").concat(streamTitle, "?expire=").concat(expireAt);
sourcePath = "/".concat(hub, "/").concat(streamTitle);
token = (0, sign_1.base64ToUrlSafe)((0, sign_1.hmacSha1)(signStr, publishKey));
rtmpPushUrl = "rtmp://".concat(publishDomain).concat(signStr, "&token=").concat(token);
t = expireAt.toString(16).toLowerCase();
playSign = ts_md5_1.Md5.hashStr(playKey + sourcePath + t).toString().toLowerCase();
rtmpPlayUrl = "https://".concat(playDomain).concat(sourcePath, ".m3u8?sign=").concat(playSign, "&t=").concat(t);
pcPushUrl = "rtmp://".concat(publishDomain, "/").concat(hub, "/");
streamKey = "".concat(streamTitle, "?expire=").concat(expireAt, "&token=").concat(token);
return [2 /*return*/, {
streamTitle: streamTitle,
hub: hub,
rtmpPushUrl: rtmpPushUrl,
rtmpPlayUrl: rtmpPlayUrl,
pcPushUrl: pcPushUrl,
streamKey: streamKey,
expireAt: expireAt,
}];
});
});
}
// 生成直播回放
function getPlayBackUrl(params, context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var streamTitle, start, end, config, hub, playBackDomain, encodeStreamTitle, path, bodyStr, contentType, token, url;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
streamTitle = params.streamTitle, start = params.start, end = params.end;
return [4 /*yield*/, getQiniuUploadInfo(context)];
case 1:
config = _a.sent();
hub = config.hub, playBackDomain = config.playBackDomain;
encodeStreamTitle = (0, sign_1.base64ToUrlSafe)(streamTitle);
path = "/v2/hubs/".concat(hub, "/streams/").concat(encodeStreamTitle, "/saveas");
bodyStr = JSON.stringify({
fname: streamTitle,
start: start,
end: end,
});
contentType = 'application/json';
return [4 /*yield*/, getQiniuToken(config, {
method: 'POST',
path: path,
contentType: contentType,
bodyStr: bodyStr,
})];
case 2:
token = (_a.sent()).token;
url = "https://pili.qiniuapi.com".concat(path);
fetch(url, {
method: 'POST',
headers: {
Authorization: token,
'Content-Type': contentType,
},
body: bodyStr,
mode: 'no-cors',
})
.then(function (res) {
console.log(res.json());
}).then(function (res) {
console.log(res);
}).catch(function (e) {
console.log(e);
});
return [2 /*return*/, "https://".concat(playBackDomain, "/").concat(streamTitle, ".m3u8")];
}
});
});
}
exports.getPlayBackUrl = getPlayBackUrl;

View File

@ -447,7 +447,7 @@ exports.default = OakComponent({
types_1.OakUnloggedInException.name) {
this.navigateTo({
url: '/login',
}, undefined, true);
}, undefined);
return [2 /*return*/];
}
throw error_2;

View File

@ -13,10 +13,15 @@ export declare type WebConfig = {
appId?: string;
appSecret?: string;
};
declare type WechatPublicTemplateMsgsConfig = Record<string, {
templateId: string;
dataDef: [string, string][];
}>;
export declare type WechatPublicConfig = {
type: 'wechatPublic';
appId: string;
appSecret: string;
templateMsgs?: WechatPublicTemplateMsgsConfig;
};
export interface Schema extends EntityShape {
name: String<32>;
@ -25,3 +30,4 @@ export interface Schema extends EntityShape {
system: System;
config: WebConfig | WechatMpConfig | WechatPublicConfig;
}
export {};

6
lib/entities/MessageType.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
import { String, Text } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
export interface Schema extends EntityShape {
name: String<24>;
template: Text;
}

View File

@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
;
var locale = {
zh_CN: {
attr: {
name: '名称',
template: '模板',
},
},
};

View File

@ -1,21 +1,8 @@
import { String, Text } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { QiniuConfig } from '../types/Config';
export declare type PlatformConfig = {
Cos?: {
qiniu?: QiniuConfig;
};
Map?: {
amap?: {
webApiKey: string;
};
};
UserEntityGrant?: {
lifetimeLength: number;
};
};
import { Config } from '../types/Config';
export interface Schema extends EntityShape {
name: String<32>;
description: Text;
config: PlatformConfig;
config: Config;
}

View File

@ -1,24 +1,11 @@
import { String, Boolean, Text } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { Schema as Platform } from './Platform';
import { QiniuConfig } from '../types/Config';
export declare type SystemConfig = {
Cos?: {
qiniu?: QiniuConfig;
};
Map?: {
amap?: {
webApiKey: string;
};
};
UserEntityGrant?: {
lifetimeLength: number;
};
};
import { Config } from '../types/Config';
export interface Schema extends EntityShape {
name: String<32>;
description: Text;
config: SystemConfig;
config: Config;
platform: Platform;
super?: Boolean;
}

View File

@ -6,7 +6,7 @@ import { EntityDict } from '../general-app-domain';
import { RuntimeContext } from '../context/RuntimeContext';
export declare class ExtraFile<ED extends EntityDict, Cxt extends RuntimeContext<ED>, AD extends AspectDict<ED, Cxt>> extends Feature<ED, Cxt, AD & CommonAspectDict<ED, Cxt>> {
constructor(aspectWrapper: AspectWrapper<ED, Cxt, AD & CommonAspectDict<ED, Cxt>>);
getUploadInfo(origin: string, key?: string): Promise<import("oak-frontend-base/lib/types/Upload").QiniuUploadInfo>;
private getUploadInfo;
upload(extraFile: DeduceCreateOperationData<EntityDict['extraFile']['OpSchema']>): Promise<{
url: string;
bucket: string;

View File

@ -58,9 +58,6 @@ var ExtraFile = /** @class */ (function (_super) {
});
});
};
tslib_1.__decorate([
Feature_1.Action
], ExtraFile.prototype, "getUploadInfo", null);
tslib_1.__decorate([
Feature_1.Action
], ExtraFile.prototype, "upload", null);

View File

@ -20,10 +20,18 @@ export declare type WebConfig = {
appId?: string;
appSecret?: string;
};
declare type WechatPublicTemplateMsgsConfig = Record<string, {
templateId: string;
dataDef: [
string,
string
][];
}>;
export declare type WechatPublicConfig = {
type: 'wechatPublic';
appId: string;
appSecret: string;
templateMsgs?: WechatPublicTemplateMsgsConfig;
};
export declare type OpSchema = {
id: PrimaryKey;

View File

@ -12,6 +12,7 @@ import { EntityDef as Domain } from "./Domain/Schema";
import { EntityDef as Email } from "./Email/Schema";
import { EntityDef as ExtraFile } from "./ExtraFile/Schema";
import { EntityDef as Livestream } from "./Livestream/Schema";
import { EntityDef as MessageType } from "./MessageType/Schema";
import { EntityDef as Mobile } from "./Mobile/Schema";
import { EntityDef as Platform } from "./Platform/Schema";
import { EntityDef as UserRole } from "./UserRole/Schema";
@ -36,6 +37,7 @@ export declare type EntityDict = {
email: Email;
extraFile: ExtraFile;
livestream: Livestream;
messageType: MessageType;
mobile: Mobile;
platform: Platform;
userRole: UserRole;

View File

@ -0,0 +1,102 @@
import { String, Text, Datetime, PrimaryKey } from "oak-domain/lib/types/DataType";
import { Q_DateValue, Q_StringValue, NodeId, MakeFilter, ExprOp, ExpressionKey } from "oak-domain/lib/types/Demand";
import { OneOf } from "oak-domain/lib/types/Polyfill";
import * as SubQuery from "../_SubQuery";
import { FormCreateData, FormUpdateData, Operation as OakOperation, MakeAction as OakMakeAction } from "oak-domain/lib/types/Entity";
import { GenericAction } from "oak-domain/lib/actions/action";
export declare type OpSchema = {
id: PrimaryKey;
$$createAt$$: Datetime;
$$updateAt$$: Datetime;
$$deleteAt$$?: Datetime | null;
name: String<24>;
template: Text;
};
export declare type OpAttr = keyof OpSchema;
export declare type Schema = {
id: PrimaryKey;
$$createAt$$: Datetime;
$$updateAt$$: Datetime;
$$deleteAt$$?: Datetime | null;
name: String<24>;
template: Text;
} & {
[A in ExpressionKey]?: any;
};
declare type AttrFilter = {
id: Q_StringValue | SubQuery.MessageTypeIdSubQuery;
$$createAt$$: Q_DateValue;
$$updateAt$$: Q_DateValue;
name: Q_StringValue;
template: Q_StringValue;
};
export declare type Filter = MakeFilter<AttrFilter & ExprOp<OpAttr | string>>;
export declare type Projection = {
"#id"?: NodeId;
[k: string]: any;
id: 1;
$$createAt$$?: 1;
$$updateAt$$?: 1;
name?: 1;
template?: 1;
} & Partial<ExprOp<OpAttr | string>>;
export declare type ExportProjection = {
"#id"?: NodeId;
[k: string]: any;
id?: string;
$$createAt$$?: string;
$$updateAt$$?: string;
name?: string;
template?: string;
} & Partial<ExprOp<OpAttr | string>>;
declare type MessageTypeIdProjection = OneOf<{
id: 1;
}>;
export declare type SortAttr = {
id: 1;
} | {
$$createAt$$: 1;
} | {
$$updateAt$$: 1;
} | {
name: 1;
} | {
template: 1;
} | {
[k: string]: any;
} | OneOf<ExprOp<OpAttr | string>>;
export declare type SortNode = {
$attr: SortAttr;
$direction?: "asc" | "desc";
};
export declare type Sorter = SortNode[];
export declare type SelectOperation<P extends Object = Projection> = Omit<OakOperation<"select", P, Filter, Sorter>, "id">;
export declare type Selection<P extends Object = Projection> = Omit<SelectOperation<P>, "action">;
export declare type Exportation = OakOperation<"export", ExportProjection, Filter, Sorter>;
export declare type CreateOperationData = FormCreateData<OpSchema>;
export declare type CreateSingleOperation = OakOperation<"create", CreateOperationData>;
export declare type CreateMultipleOperation = OakOperation<"create", Array<CreateOperationData>>;
export declare type CreateOperation = CreateSingleOperation | CreateMultipleOperation;
export declare type UpdateOperationData = FormUpdateData<OpSchema> & {
[k: string]: any;
};
export declare type UpdateOperation = OakOperation<"update" | string, UpdateOperationData, Filter, Sorter>;
export declare type RemoveOperationData = {};
export declare type RemoveOperation = OakOperation<"remove", RemoveOperationData, Filter, Sorter>;
export declare type Operation = CreateOperation | UpdateOperation | RemoveOperation | SelectOperation;
export declare type MessageTypeIdSubQuery = Selection<MessageTypeIdProjection>;
export declare type NativeAttr = OpAttr;
export declare type FullAttr = NativeAttr;
export declare type EntityDef = {
Schema: Schema;
OpSchema: OpSchema;
Action: OakMakeAction<GenericAction> | string;
Selection: Selection;
Operation: Operation;
Create: CreateOperation;
Update: UpdateOperation;
Remove: RemoveOperation;
CreateSingle: CreateSingleOperation;
CreateMulti: CreateMultipleOperation;
};
export {};

View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -0,0 +1,3 @@
import { StorageDesc } from "oak-domain/lib/types/Storage";
import { OpSchema } from "./Schema";
export declare const desc: StorageDesc<OpSchema>;

View File

@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.desc = void 0;
var action_1 = require("oak-domain/lib/actions/action");
exports.desc = {
attributes: {
name: {
type: "varchar",
params: {
length: 24
}
},
template: {
type: "text"
}
},
actionType: "crud",
actions: action_1.genericActions
};

View File

@ -0,0 +1 @@
{ "attr": { "name": "名称", "template": "模板" } }

View File

@ -4,21 +4,8 @@ import { OneOf } from "oak-domain/lib/types/Polyfill";
import * as SubQuery from "../_SubQuery";
import { FormCreateData, FormUpdateData, Operation as OakOperation, MakeAction as OakMakeAction } from "oak-domain/lib/types/Entity";
import { GenericAction } from "oak-domain/lib/actions/action";
import { QiniuConfig } from "../../types/Config";
import { Config } from "../../types/Config";
import * as System from "../System/Schema";
export declare type PlatformConfig = {
Cos?: {
qiniu?: QiniuConfig;
};
Map?: {
amap?: {
webApiKey: string;
};
};
UserEntityGrant?: {
lifetimeLength: number;
};
};
export declare type OpSchema = {
id: PrimaryKey;
$$createAt$$: Datetime;
@ -26,7 +13,7 @@ export declare type OpSchema = {
$$deleteAt$$?: Datetime | null;
name: String<32>;
description: Text;
config: PlatformConfig;
config: Config;
};
export declare type OpAttr = keyof OpSchema;
export declare type Schema = {
@ -36,7 +23,7 @@ export declare type Schema = {
$$deleteAt$$?: Datetime | null;
name: String<32>;
description: Text;
config: PlatformConfig;
config: Config;
system$platform?: Array<System.Schema>;
} & {
[A in ExpressionKey]?: any;
@ -47,7 +34,7 @@ declare type AttrFilter = {
$$updateAt$$: Q_DateValue;
name: Q_StringValue;
description: Q_StringValue;
config: Q_EnumValue<PlatformConfig>;
config: Q_EnumValue<Config>;
};
export declare type Filter = MakeFilter<AttrFilter & ExprOp<OpAttr | string>>;
export declare type Projection = {

View File

@ -15,15 +15,16 @@ var Storage_11 = require("./Domain/Storage");
var Storage_12 = require("./Email/Storage");
var Storage_13 = require("./ExtraFile/Storage");
var Storage_14 = require("./Livestream/Storage");
var Storage_15 = require("./Mobile/Storage");
var Storage_16 = require("./Platform/Storage");
var Storage_17 = require("./UserRole/Storage");
var Storage_18 = require("./Role/Storage");
var Storage_19 = require("./System/Storage");
var Storage_20 = require("./Token/Storage");
var Storage_21 = require("./UserEntityGrant/Storage");
var Storage_22 = require("./WechatQrCode/Storage");
var Storage_23 = require("./WechatUser/Storage");
var Storage_15 = require("./MessageType/Storage");
var Storage_16 = require("./Mobile/Storage");
var Storage_17 = require("./Platform/Storage");
var Storage_18 = require("./UserRole/Storage");
var Storage_19 = require("./Role/Storage");
var Storage_20 = require("./System/Storage");
var Storage_21 = require("./Token/Storage");
var Storage_22 = require("./UserEntityGrant/Storage");
var Storage_23 = require("./WechatQrCode/Storage");
var Storage_24 = require("./WechatUser/Storage");
exports.storageSchema = {
modi: Storage_1.desc,
modiEntity: Storage_2.desc,
@ -39,13 +40,14 @@ exports.storageSchema = {
email: Storage_12.desc,
extraFile: Storage_13.desc,
livestream: Storage_14.desc,
mobile: Storage_15.desc,
platform: Storage_16.desc,
userRole: Storage_17.desc,
role: Storage_18.desc,
system: Storage_19.desc,
token: Storage_20.desc,
userEntityGrant: Storage_21.desc,
wechatQrCode: Storage_22.desc,
wechatUser: Storage_23.desc
messageType: Storage_15.desc,
mobile: Storage_16.desc,
platform: Storage_17.desc,
userRole: Storage_18.desc,
role: Storage_19.desc,
system: Storage_20.desc,
token: Storage_21.desc,
userEntityGrant: Storage_22.desc,
wechatQrCode: Storage_23.desc,
wechatUser: Storage_24.desc
};

View File

@ -4,24 +4,11 @@ import { OneOf } from "oak-domain/lib/types/Polyfill";
import * as SubQuery from "../_SubQuery";
import { FormCreateData, FormUpdateData, Operation as OakOperation, MakeAction as OakMakeAction } from "oak-domain/lib/types/Entity";
import { GenericAction } from "oak-domain/lib/actions/action";
import { QiniuConfig } from "../../types/Config";
import { Config } from "../../types/Config";
import * as Platform from "../Platform/Schema";
import * as Application from "../Application/Schema";
import * as Domain from "../Domain/Schema";
import * as User from "../User/Schema";
export declare type SystemConfig = {
Cos?: {
qiniu?: QiniuConfig;
};
Map?: {
amap?: {
webApiKey: string;
};
};
UserEntityGrant?: {
lifetimeLength: number;
};
};
export declare type OpSchema = {
id: PrimaryKey;
$$createAt$$: Datetime;
@ -29,7 +16,7 @@ export declare type OpSchema = {
$$deleteAt$$?: Datetime | null;
name: String<32>;
description: Text;
config: SystemConfig;
config: Config;
platformId: ForeignKey<"platform">;
super?: Boolean | null;
};
@ -41,7 +28,7 @@ export declare type Schema = {
$$deleteAt$$?: Datetime | null;
name: String<32>;
description: Text;
config: SystemConfig;
config: Config;
platformId: ForeignKey<"platform">;
super?: Boolean | null;
platform: Platform.Schema;
@ -57,7 +44,7 @@ declare type AttrFilter = {
$$updateAt$$: Q_DateValue;
name: Q_StringValue;
description: Q_StringValue;
config: Q_EnumValue<SystemConfig>;
config: Q_EnumValue<Config>;
platformId: Q_StringValue | SubQuery.PlatformIdSubQuery;
platform: Platform.Filter;
super: Q_BooleanValue;

View File

@ -12,6 +12,7 @@ import * as Domain from "./Domain/Schema";
import * as Email from "./Email/Schema";
import * as ExtraFile from "./ExtraFile/Schema";
import * as Livestream from "./Livestream/Schema";
import * as MessageType from "./MessageType/Schema";
import * as Mobile from "./Mobile/Schema";
import * as Platform from "./Platform/Schema";
import * as UserRole from "./UserRole/Schema";
@ -121,6 +122,11 @@ export declare type LivestreamIdSubQuery = {
entity: "livestream";
}) | any;
};
export declare type MessageTypeIdSubQuery = {
[K in "$in" | "$nin"]?: (MessageType.MessageTypeIdSubQuery & {
entity: "messageType";
}) | any;
};
export declare type MobileIdSubQuery = {
[K in "$in" | "$nin"]?: (Mobile.MobileIdSubQuery & {
entity: "mobile";

80
lib/types/Config.d.ts vendored
View File

@ -1,15 +1,79 @@
export declare type QiniuConfig = {
export declare type QiniuCloudConfig = {
accessKey: string;
secretKey: string;
};
export declare type QiniuLiveConfig = {
accessKey: string;
liveHost: string;
publishDomain: string;
playDomain: string;
playBackDomain: string;
hub: string;
publishKey: string;
playKey: string;
};
export declare type QiniuCosConfig = {
accessKey: string;
uploadHost: string;
liveHost?: string;
puhlishDomain?: string;
playDomain?: string;
playBackDomain?: string;
hub?: string;
publisthKey?: string;
playKey?: string;
bucket: string;
domain: string;
protocol: string | string[];
};
export declare type AliCloudConfig = {
accessKeyId: string;
accessKeySecret: string;
regionId: string;
};
export declare type TencentCloudConfig = {
secretId: string;
secretKey: string;
region: string;
};
export declare type AmapCloudConfig = {
webApiKey: string;
};
export declare type AliSmsConfig = {
accessKeyId: string;
defaultSignName: string;
templates: Record<string, {
signName?: string;
code: string;
params: string[];
}>;
};
export declare type TencentSmsConfig = {
secretId: string;
defaultSignName: string;
templates: Record<string, {
signName?: string;
code: string;
}>;
};
export declare type Config = {
Account?: {
ali?: AliCloudConfig[];
tencent?: TencentCloudConfig[];
qiniu?: QiniuCloudConfig[];
amap?: AmapCloudConfig[];
};
Cos?: {
qiniu?: QiniuCosConfig;
};
Live?: {
qiniu?: QiniuLiveConfig;
};
Map?: {
amap?: {
webApiKey: string;
};
};
UserEntityGrant?: {
lifetimeLength: number;
};
Sms?: {
ali?: AliSmsConfig[];
tencent?: TencentSmsConfig[];
};
};
export declare type Origin = 'ali' | 'tencent' | 'qiniu' | 'amap';
export declare type Service = 'Map' | 'Cos' | 'Live' | 'Sms';

View File

@ -1,6 +1,6 @@
import { OpSchema as ExtraFile } from '../general-app-domain/ExtraFile/Schema';
import { SystemConfig } from '../general-app-domain/System/Schema';
export declare function composeFileUrl(extraFile: Pick<ExtraFile, 'type' | 'bucket' | 'filename' | 'origin' | 'extra1' | 'objectId' | 'extension' | 'entity'>, systemConfig?: SystemConfig): any;
import { Config } from '../types/Config';
export declare function composeFileUrl(extraFile: Pick<ExtraFile, 'type' | 'bucket' | 'filename' | 'origin' | 'extra1' | 'objectId' | 'extension' | 'entity'>, config?: Config): any;
export declare function decomposeFileUrl(url: string): Pick<ExtraFile, 'bucket' | 'filename' | 'origin' | 'type' | 'extra1'>;
export declare function getFileURL(file: File): any;
export declare function bytesToSize(sizes: any): any;

View File

@ -1,7 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.bytesToSize = exports.getFileURL = exports.decomposeFileUrl = exports.composeFileUrl = void 0;
function composeFileUrl(extraFile, systemConfig) {
function composeFileUrl(extraFile, config) {
var type = extraFile.type, bucket = extraFile.bucket, filename = extraFile.filename, origin = extraFile.origin, extra1 = extraFile.extra1, objectId = extraFile.objectId, extension = extraFile.extension, entity = extraFile.entity;
if (extra1) {
// 有extra1就用extra1 可能File对象
@ -13,8 +13,8 @@ function composeFileUrl(extraFile, systemConfig) {
}
return extra1;
}
if (systemConfig && systemConfig.Cos) {
var _a = systemConfig.Cos[origin], domain = _a.domain, protocol = _a.protocol;
if (config && config.Cos) {
var _a = config.Cos[origin], domain = _a.domain, protocol = _a.protocol;
var protocol2 = protocol;
if (protocol instanceof Array) {
// protocol存在https 说明域名有证书

10
lib/utils/getContextConfig.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
import { RuntimeContext } from '../context/RuntimeContext';
import { EntityDict } from '../general-app-domain';
import { Origin, Service } from '../types/Config';
export declare function getConfig<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(context: Cxt, service: Service, origin: Origin): Promise<{
instance: import("oak-external-sdk").QiniuCloudInstance;
config: any;
} | {
instance: import("oak-external-sdk").AmapInstance;
config: any;
}>;

View File

@ -0,0 +1,83 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getConfig = void 0;
var tslib_1 = require("tslib");
var console_1 = require("console");
var Exception_1 = require("oak-domain/lib/types/Exception");
var oak_external_sdk_1 = require("oak-external-sdk");
function getConfig(context, service, origin) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var rowStore, systemId, _a, system, _b, systemConfig, platform, originConfig, originCloudAccounts, platformConfig, aliAccount, tencentAccount, qiniuAccount, qiniuInstance, amapAccount, amapInstance;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
rowStore = context.rowStore;
return [4 /*yield*/, context.getSystemId()];
case 1:
systemId = _c.sent();
(0, console_1.assert)(systemId);
return [4 /*yield*/, rowStore.select('system', {
data: {
id: 1,
config: 1,
platform: {
id: 1,
config: 1,
}
},
filter: {
id: systemId
}
}, context, {
dontCollect: true,
})];
case 2:
_a = tslib_1.__read.apply(void 0, [(_c.sent()).result, 1]), system = _a[0];
_b = system, systemConfig = _b.config, platform = _b.platform;
originConfig = systemConfig[service] && systemConfig[service][origin];
originCloudAccounts = originConfig && systemConfig.Account && systemConfig.Account[origin];
if (!originConfig) {
platformConfig = platform.config;
originConfig = platformConfig[service] && platformConfig[service][origin];
originCloudAccounts = originConfig && platformConfig.Account && platformConfig.Account[origin];
}
if (!originConfig) {
throw new Exception_1.OakDataException("\u8C03\u7528\u7684\u670D\u52A1".concat(service, "\u6E90").concat(origin, "\u627E\u4E0D\u5230\u76F8\u5E94\u7684\u914D\u7F6E\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458"));
}
switch (origin) {
case 'ali': {
aliAccount = originCloudAccounts.find(function (ele) { return ele.accessKeyId === originConfig.accessKeyId; });
(0, console_1.assert)(aliAccount, "\u8C03\u7528\u7684\u670D\u52A1".concat(service, "\u6E90").concat(origin, "\u627E\u4E0D\u5230\u76F8\u5E94\u7684\u4E91\u5E73\u53F0\u5E10\u53F7\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458"));
throw new Error('阿里云的external SDK还未实现');
}
case 'tencent': {
tencentAccount = originCloudAccounts.find(function (ele) { return ele.secretId === originConfig.secretId; });
(0, console_1.assert)(tencentAccount, "\u8C03\u7528\u7684\u670D\u52A1".concat(service, "\u6E90").concat(origin, "\u627E\u4E0D\u5230\u76F8\u5E94\u7684\u4E91\u5E73\u53F0\u5E10\u53F7\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458"));
throw new Error('腾讯云的external SDK还未实现');
}
case 'qiniu': {
qiniuAccount = originCloudAccounts.find(function (ele) { return ele.accessKey === originConfig.accessKey; });
(0, console_1.assert)(qiniuAccount, "\u8C03\u7528\u7684\u670D\u52A1".concat(service, "\u6E90").concat(origin, "\u627E\u4E0D\u5230\u76F8\u5E94\u7684\u4E91\u5E73\u53F0\u5E10\u53F7\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458"));
qiniuInstance = oak_external_sdk_1.QiniuSDK.getInstance(qiniuAccount.accessKey, qiniuAccount.secretKey);
return [2 /*return*/, {
instance: qiniuInstance,
config: originConfig,
}];
}
default: {
(0, console_1.assert)(origin === 'amap');
amapAccount = originCloudAccounts.find(function (ele) { return ele.webApiKey === originConfig.webApiKey; });
(0, console_1.assert)(amapAccount, "\u8C03\u7528\u7684\u670D\u52A1".concat(service, "\u6E90").concat(origin, "\u627E\u4E0D\u5230\u76F8\u5E94\u7684\u4E91\u5E73\u53F0\u5E10\u53F7\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458"));
amapInstance = oak_external_sdk_1.AmapSDK.getInstance(amapAccount.webApiKey);
return [2 /*return*/, {
instance: amapInstance,
config: originConfig,
}];
}
}
return [2 /*return*/];
}
});
});
}
exports.getConfig = getConfig;

View File

@ -1,6 +1,7 @@
import { RuntimeContext } from '../context/RuntimeContext';
import { EntityDict } from '../general-app-domain';
import { Schema as Livestream } from '../general-app-domain/Livestream/Schema';
import { Origin } from '../types/Config';
/**
*
* @param streamTitle
@ -9,6 +10,7 @@ import { Schema as Livestream } from '../general-app-domain/Livestream/Schema';
* @returns Livestream
*/
export declare function getLivestream<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(params: {
origin: Origin;
streamTitle: string;
expireAt: number;
}, context: Cxt): Promise<Pick<Livestream, 'streamTitle' | 'hub' | 'rtmpPushUrl' | 'rtmpPlayUrl' | 'pcPushUrl' | 'streamKey' | 'expireAt'>>;
@ -19,11 +21,13 @@ export declare function getLivestream<ED extends EntityDict, Cxt extends Runtime
* @param context context
* @returns livestream对象
*/
export declare function getLivestream2<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(params: {
export declare function getStreamObj<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(params: {
origin: Origin;
streamTitle: string;
expireAt: number;
}, context: Cxt): Promise<Pick<Livestream, 'streamTitle' | 'hub' | 'rtmpPushUrl' | 'rtmpPlayUrl' | 'pcPushUrl' | 'streamKey' | 'expireAt'>>;
export declare function getPlayBackUrl<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(params: {
origin: Origin;
streamTitle: string;
start: number;
end: number;

75
lib/utils/livestream.js Normal file
View File

@ -0,0 +1,75 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPlayBackUrl = exports.getStreamObj = exports.getLivestream = void 0;
var tslib_1 = require("tslib");
var getContextConfig_1 = require("./getContextConfig");
var assert_1 = tslib_1.__importDefault(require("assert"));
/**
* 创建直播流并生成推拉流地址
* @param streamTitle 直播流名称
* @param expireAt 推流过期时间
* @param context context
* @returns Livestream 对象
*/
function getLivestream(params, context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var streamTitle, expireAt, origin, _a, instance, config, _b, hub, liveHost, publishDomain, playDomain, playKey, publishKey;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
streamTitle = params.streamTitle, expireAt = params.expireAt, origin = params.origin;
return [4 /*yield*/, (0, getContextConfig_1.getConfig)(context, 'Live', origin)];
case 1:
_a = _c.sent(), instance = _a.instance, config = _a.config;
(0, assert_1.default)(origin === 'qiniu');
_b = config, hub = _b.hub, liveHost = _b.liveHost, publishDomain = _b.publishDomain, playDomain = _b.playDomain, playKey = _b.playKey, publishKey = _b.publishKey;
return [2 /*return*/, instance.getLiveStream(hub, 'POST', streamTitle, liveHost, publishDomain, playDomain, publishKey, playKey, expireAt)];
}
});
});
}
exports.getLivestream = getLivestream;
// 获取推拉流地址
/**
* 直播流已存在的情况下获取推拉流地址
* @param streamTitle 直播流名称
* @param expireAt 推流过期时间
* @param context context
* @returns livestream对象
*/
function getStreamObj(params, context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var streamTitle, expireAt, origin, _a, instance, config, _b, publishDomain, publishKey, playDomain, playKey, hub;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
streamTitle = params.streamTitle, expireAt = params.expireAt, origin = params.origin;
return [4 /*yield*/, (0, getContextConfig_1.getConfig)(context, 'Live', origin)];
case 1:
_a = _c.sent(), instance = _a.instance, config = _a.config;
(0, assert_1.default)(origin === 'qiniu');
_b = config, publishDomain = _b.publishDomain, publishKey = _b.publishKey, playDomain = _b.playDomain, playKey = _b.playKey, hub = _b.hub;
return [2 /*return*/, instance.getStreamObj(publishDomain, playDomain, hub, publishKey, playKey, streamTitle, expireAt)];
}
});
});
}
exports.getStreamObj = getStreamObj;
// 生成直播回放
function getPlayBackUrl(params, context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var streamTitle, start, end, origin, _a, config, instance, _b, hub, playBackDomain, liveHost;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
streamTitle = params.streamTitle, start = params.start, end = params.end, origin = params.origin;
return [4 /*yield*/, (0, getContextConfig_1.getConfig)(context, 'Live', origin)];
case 1:
_a = _c.sent(), config = _a.config, instance = _a.instance;
_b = config, hub = _b.hub, playBackDomain = _b.playBackDomain, liveHost = _b.liveHost;
return [2 /*return*/, instance.getPlayBackUrl(hub, playBackDomain, streamTitle, start, end, 'POST', liveHost)];
}
});
});
}
exports.getPlayBackUrl = getPlayBackUrl;

View File

@ -21,7 +21,6 @@
"oak-frontend-base": "file:../oak-frontend-base",
"oak-memory-tree-store": "file:../oak-memory-tree-store",
"sha1": "^1.1.1",
"ts-md5": "^1.3.1",
"tslib": "^2.4.0",
"uuid": "^8.3.2"
},

View File

@ -4,115 +4,73 @@ import { EntityDict } from "../general-app-domain";
import { QiniuUploadInfo } from "oak-frontend-base/lib/types/Upload";
import { RuntimeContext } from '../context/RuntimeContext';
import { Schema as Livestream } from '../general-app-domain/Livestream/Schema';
import { Origin } from "../types/Config";
type GeneralAspectDict<
ED extends EntityDict,
Cxt extends RuntimeContext<ED>
> = {
loginByMobile: (
params: {
captcha?: string;
password?: string;
mobile: string;
env: WebEnv | WechatMpEnv;
},
context: Cxt
) => Promise<string>;
loginWechat: (
{
code,
env,
}: {
code: string;
env: WebEnv;
},
context: Cxt
) => Promise<string>;
loginWechatMp: (
{
code,
env,
}: {
code: string;
env: WechatMpEnv;
},
context: Cxt
) => Promise<string>;
syncUserInfoWechatMp: (
{
nickname,
avatarUrl,
encryptedData,
iv,
signature,
}: {
nickname: string;
avatarUrl: string;
encryptedData: string;
iv: string;
signature: string;
},
context: Cxt
) => Promise<void>;
getUploadInfo: (
params: { origin: string; key?: string },
context: Cxt
) => Promise<QiniuUploadInfo>;
getLivestream: (
params: {
streamTitle: string;
expireAt: number;
},
context: Cxt
) => Promise<
Pick<
Livestream,
| 'streamTitle'
| 'hub'
| 'rtmpPushUrl'
| 'rtmpPlayUrl'
| 'pcPushUrl'
| 'streamKey'
| 'expireAt'
>
>;
getLivestream2: (
params: {
streamTitle: string;
expireAt: number;
},
context: Cxt
) => Promise<
Pick<
Livestream,
| 'streamTitle'
| 'hub'
| 'rtmpPushUrl'
| 'rtmpPlayUrl'
| 'pcPushUrl'
| 'streamKey'
| 'expireAt'
>
>;
getPlayBackUrl: (
> = {
loginByMobile: (
params: {
streamTitle: string,
start: number,
end: number,
captcha?: string;
password?: string;
mobile: string;
env: WebEnv | WechatMpEnv;
},
context: Cxt
) => Promise<string>;
sendCaptcha: (params: {
mobile: string;
env: WechatMpEnv | WebEnv;
}) => Promise<string>;
getApplication: (
params: {
type: AppType;
},
context: Cxt
) => Promise<string>;
};
loginWechat: (
{
code,
env,
}: {
code: string;
env: WebEnv;
},
context: Cxt
) => Promise<string>;
loginWechatMp: (
{
code,
env,
}: {
code: string;
env: WechatMpEnv;
},
context: Cxt
) => Promise<string>;
syncUserInfoWechatMp: (
{
nickname,
avatarUrl,
encryptedData,
iv,
signature,
}: {
nickname: string;
avatarUrl: string;
encryptedData: string;
iv: string;
signature: string;
},
context: Cxt
) => Promise<void>;
getUploadInfo: (
params: { origin: Origin; bucket?: string; key?: string },
context: Cxt
) => Promise<QiniuUploadInfo>;
sendCaptcha: (params: {
mobile: string;
env: WechatMpEnv | WebEnv;
}) => Promise<string>;
getApplication: (
params: {
type: AppType;
},
context: Cxt
) => Promise<string>;
};
export type AspectDict<ED extends EntityDict, Cxt extends RuntimeContext<ED>> = GeneralAspectDict<ED, Cxt>;

View File

@ -1,44 +1,22 @@
import { RuntimeContext } from '../context/RuntimeContext';
import { EntityDict } from '../general-app-domain';
import { SystemConfig } from '../general-app-domain/System/Schema';
import qiniuInstance from '../utils/externalUpload/qiniu';
import { Origin, QiniuCosConfig } from '../types/Config';
import { QiniuUploadInfo } from 'oak-frontend-base/lib/types/Upload';
import { getConfig } from '../utils/getContextConfig';
import { assert } from 'console';
import { QiniuCloudInstance } from 'oak-external-sdk';
const ExternalUploadClazz = {
qiniu: qiniuInstance,
};
export async function getUploadInfo<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(
params: { origin: string, key?: string },
params: { origin: Origin; bucket?: string; key?: string },
context: Cxt): Promise<QiniuUploadInfo> {
const { rowStore } = context;
const application = await context.getApplication();
const { type, config, systemId } = application!;
const { origin, key } = params;
const { origin, key, bucket } = params;
const { result: [system] } = await rowStore.select('system', {
data: {
id: 1,
config: 1
},
filter: {
id: systemId
}
}, context, {
dontCollect: true,
});
try {
const { config: systemConfig } = system;
const originConfig = (systemConfig as SystemConfig).Cos![
origin as keyof typeof systemConfig
];
const instance = new ExternalUploadClazz[
origin as keyof typeof ExternalUploadClazz
](originConfig);
const uploadInfo = instance.getUploadInfo(key);
return uploadInfo;
} catch (err) {
throw err;
}
const {
instance,
config,
} = await getConfig<ED, Cxt>(context, 'Cos', origin);
assert(origin === 'qiniu');
const { uploadHost, domain, bucket: bucket2 } = config as QiniuCosConfig;
return (instance as QiniuCloudInstance).getUploadInfo(uploadHost, domain, bucket || bucket2, key);
}

View File

@ -7,7 +7,6 @@ import {
} from './token';
import { getUploadInfo } from './extraFile';
import { getApplication } from './application';
import { getLivestream, getLivestream2, getPlayBackUrl } from './livestream';
// import commonAspectDict from 'oak-common-aspect';
export const aspectDict = {
@ -18,9 +17,6 @@ export const aspectDict = {
getUploadInfo,
sendCaptcha,
getApplication,
getLivestream,
getLivestream2,
getPlayBackUrl,
};
// export type AspectDict<ED extends EntityDict & BaseEntityDict> = TokenAD<ED> & CrudAD<ED>;

View File

@ -1,259 +0,0 @@
import { RuntimeContext } from '../context/RuntimeContext';
import { EntityDict } from '../general-app-domain';
import { SystemConfig } from '../general-app-domain/System/Schema';
import { Schema as Livestream } from '../general-app-domain/Livestream/Schema';
import QiniuLive from '../utils/externalUpload/qiniu_live';
import { hmacSha1, base64ToUrlSafe } from '../utils/sign';
import { Md5 } from 'ts-md5';
async function getQiniuUploadInfo<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(
context: Cxt
) {
// 请求鉴权
const { rowStore } = context;
const application = await context.getApplication();
const { type, config, systemId } = application!;
const origin = "qiniu";
const { result: [system] } = await rowStore.select('system', {
data: {
id: 1,
config: 1
},
filter: {
id: systemId
}
}, context, {
dontCollect: true,
});
try {
const { config: systemConfig } = system as { config: SystemConfig };
const originConfig = systemConfig.Cos![
origin as keyof SystemConfig['Cos']
];
return originConfig;
} catch (err) {
throw err;
}
}
async function getQiniuToken(
config: { liveHost:string, accessKey: string, secretKey:string, hub:string },
params: { method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, rawQuery?: string, contentType?: string, bodyStr?: string, },
): Promise<{ token: string }> {
// 获取请求信息
const { method, path, rawQuery, contentType, bodyStr } = params;
const { liveHost, accessKey, secretKey } = config;
// 请求鉴权
try{
const instance = new QiniuLive({
accessKey,
secretKey,
host: liveHost,
method,
path,
rawQuery,
contentType,
bodyStr,
})
const token = instance.getToken();
// 拿到鉴权Token
return {
token,
};
} catch (err) {
throw err;
}
}
/**
*
* @param streamTitle
* @param expireAt
* @param context context
* @returns Livestream
*/
export async function getLivestream<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(
params: {
streamTitle: string,
expireAt: number,
},
context: Cxt
): Promise<Pick<Livestream,
| 'streamTitle'
| 'hub'
| 'rtmpPushUrl'
| 'rtmpPlayUrl'
| 'pcPushUrl'
| 'streamKey'
| 'expireAt'>
> {
const { streamTitle, expireAt } = params;
// 获取七牛直播云信息
const config = await getQiniuUploadInfo<ED, Cxt>(context);
const { hub } = config;
// 七牛创建直播流接口路径
const path = `/v2/hubs/${hub}/streams`;
// 如果用户没给streamTitle那么随机生成一个
let key:string = streamTitle;
if (!key) {
key = `class${new Date().getTime()}`;
}
const bodyStr = JSON.stringify({
key,
})
const contentType = 'application/json';
const { token } = await getQiniuToken(config, {
method: 'POST',
path,
contentType,
bodyStr,
});
const url = `https://pili.qiniuapi.com/v2/hubs/${hub}/streams`;
fetch(url, {
method: 'POST',
headers: {
Authorization: token,
'Content-Type': contentType,
},
body: bodyStr,
mode: 'no-cors',
})
.then((res) => {
console.log(res.json());
}).then((res) => {
console.log(res);
}).catch((e) => {
console.log(e);
})
const obj = await getStreamObj(config, streamTitle, expireAt);
return obj;
}
// 获取推拉流地址
/**
*
* @param streamTitle
* @param expireAt
* @param context context
* @returns livestream对象
*/
export async function getLivestream2<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(
params: {
streamTitle: string,
expireAt: number,
},
context: Cxt
): Promise<Pick<Livestream,
| 'streamTitle'
| 'hub'
| 'rtmpPushUrl'
| 'rtmpPlayUrl'
| 'pcPushUrl'
| 'streamKey'
| 'expireAt'>
> {
const {streamTitle, expireAt } = params;
const config = await getQiniuUploadInfo<ED, Cxt>(context);
const livestream = await getStreamObj(config, streamTitle, expireAt);
return livestream;
}
async function getStreamObj(
config: {
publishDomain: string,
playDomain: string,
hub: string,
publishKey: string,
playKey: string,
},
streamTitle: string,
expireAt: number
)
: Promise<Pick<Livestream,
|'streamTitle'
|'hub'
|'rtmpPushUrl'
|'rtmpPlayUrl'
|'pcPushUrl'
|'streamKey'
| 'expireAt'>
>
{
// 生成推流地址
const { hub, publishDomain, playDomain, publishKey, playKey } = config;
const signStr = `/${hub}/${streamTitle}?expire=${expireAt}`;
const sourcePath = `/${hub}/${streamTitle}`;
const token = base64ToUrlSafe(hmacSha1(signStr, publishKey));
const rtmpPushUrl = `rtmp://${publishDomain}${signStr}&token=${token}`
// 生成播放地址
const t = expireAt.toString(16).toLowerCase();
const playSign = Md5.hashStr(playKey + sourcePath + t).toString().toLowerCase();
const rtmpPlayUrl = `https://${playDomain}${sourcePath}.m3u8?sign=${playSign}&t=${t}`;
// obs推流需要的地址和串流密钥
const pcPushUrl = `rtmp://${publishDomain}/${hub}/`;
const streamKey = `${streamTitle}?expire=${expireAt}&token=${token}`
return {
streamTitle,
hub,
rtmpPushUrl,
rtmpPlayUrl,
pcPushUrl,
streamKey,
expireAt,
};
}
// 生成直播回放
export async function getPlayBackUrl<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(
params: {
streamTitle: string,
start: number,
end: number,
},
context: Cxt
) {
const { streamTitle, start, end } = params;
// 获取七牛直播云信息
const config = await getQiniuUploadInfo<ED, Cxt>(context);
const { hub, playBackDomain } = config;
// 七牛创建直播流接口路径
const encodeStreamTitle = base64ToUrlSafe(streamTitle);
const path = `/v2/hubs/${hub}/streams/${encodeStreamTitle}/saveas`;
const bodyStr = JSON.stringify({
fname: streamTitle,
start,
end,
})
const contentType = 'application/json';
const { token } = await getQiniuToken(config, {
method: 'POST',
path,
contentType,
bodyStr,
});
const url = `https://pili.qiniuapi.com${path}`;
fetch(url, {
method: 'POST',
headers: {
Authorization: token,
'Content-Type': contentType,
},
body: bodyStr,
mode: 'no-cors',
})
.then((res) => {
console.log(res.json());
}).then((res) => {
console.log(res);
}).catch((e) => {
console.log(e);
})
return `https://${playBackDomain}/${streamTitle}.m3u8`;
}

View File

@ -355,7 +355,6 @@ export default OakComponent({
url: '/login',
},
undefined,
true
);
return;
}

View File

@ -17,10 +17,16 @@ export type WebConfig = {
appSecret?: string; //网站 微信扫码登录
};
type WechatPublicTemplateMsgsConfig = Record<string, {
templateId: string;
dataDef: [string, string][]; // 前一个代表keyword后一个代表color
}>; // key值代表messageTypeId
export type WechatPublicConfig = {
type: 'wechatPublic';
appId: string;
appSecret: string;
templateMsgs?: WechatPublicTemplateMsgsConfig;
};
export interface Schema extends EntityShape {

View File

@ -0,0 +1,17 @@
import { String, Int, Text, Image } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { LocaleDef } from 'oak-domain/lib/types/Locale';
export interface Schema extends EntityShape {
name: String<24>;
template: Text;
};
const locale: LocaleDef<Schema, '', '', {}> = {
zh_CN: {
attr: {
name: '名称',
template: '模板',
},
},
};

View File

@ -1,26 +1,12 @@
import { String, Int, Datetime, Image, Boolean, Text } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { LocaleDef } from 'oak-domain/lib/types/Locale';
import { QiniuConfig } from '../types/Config';
export type PlatformConfig = {
Cos?: {
qiniu?: QiniuConfig;
};
Map?: {
amap?: {
webApiKey: string; // 高德访问rest服务接口的key
};
};
UserEntityGrant?: {
lifetimeLength: number; // 授权的过期时间ms
};
};
import { Config } from '../types/Config';
export interface Schema extends EntityShape {
name: String<32>;
description: Text;
config: PlatformConfig;
config: Config;
};
const locale: LocaleDef<Schema, '', '', {}> = {

View File

@ -1,27 +1,13 @@
import { String, Int, Datetime, Image, Boolean, Text } from 'oak-domain/lib/types/DataType';
import { String, Boolean, Text } from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { LocaleDef } from 'oak-domain/lib/types/Locale';
import { Schema as Platform } from './Platform';
import { QiniuConfig } from '../types/Config';
export type SystemConfig = {
Cos?: {
qiniu?: QiniuConfig;
};
Map?: {
amap?: {
webApiKey: string; // 高德访问rest服务接口的key
};
};
UserEntityGrant?: {
lifetimeLength: number; // 授权的过期时间ms
};
};
import { Config } from '../types/Config';
export interface Schema extends EntityShape {
name: String<32>;
description: Text;
config: SystemConfig;
config: Config;
platform: Platform;
super?: Boolean; // super表示是这个platform本身的系统可以操作application/system这些数据也可以访问超出本system的其它数据。
};

View File

@ -5,6 +5,7 @@ import { CommonAspectDict } from 'oak-common-aspect';
import { AspectDict } from '../aspects/AspectDict';
import { EntityDict } from '../general-app-domain';
import { RuntimeContext } from '../context/RuntimeContext';
import { Origin } from '../types/Config';
export class ExtraFile<
ED extends EntityDict,
@ -17,8 +18,7 @@ export class ExtraFile<
super(aspectWrapper);
}
@Action
async getUploadInfo(origin: string, key?: string) {
private async getUploadInfo(origin: Origin, key?: string) {
const { result: uploadInfo } = await this.getAspectWrapper().exec(
'getUploadInfo',
{

View File

@ -1,15 +1,90 @@
export type QiniuConfig = {
export type QiniuCloudConfig = {
accessKey: string;
secretKey: string;
};
export type QiniuLiveConfig = {
accessKey: string;
liveHost: string; // 七牛直播云接口域名
publishDomain: string; // 推流域名
playDomain: string; // 拉流域名
playBackDomain: string; // 直播回放存储域名
hub: string; // 直播空间名,
publishKey: string; // 直播空间限时鉴权密钥
playKey: string; // 拉流密钥
}
export type QiniuCosConfig = {
accessKey: string;
uploadHost: string; // 七牛上传域名
liveHost?: string; // 七牛直播云接口域名
puhlishDomain?: string; // 推流域名
playDomain?: string; // 拉流域名
playBackDomain?: string; // 直播回放存储域名
hub?: string; // 直播空间名,
publisthKey?: string; // 直播空间限时鉴权密钥
playKey?: string; // 拉流密钥
bucket: string;
domain: string; // 域名
protocol: string | string[];
};
}
export type AliCloudConfig = {
accessKeyId: string;
accessKeySecret: string;
regionId: string;
};
export type TencentCloudConfig = {
secretId: string;
secretKey: string;
region: string;
};
export type AmapCloudConfig = {
webApiKey: string;
}
export type AliSmsConfig = {
accessKeyId: string;
defaultSignName: string;
templates: Record<string, {
signName?: string;
code: string; // templateCode
params: string[]; // templateParams中的key值(和messageType中的参数位置对应)
}>;
};
export type TencentSmsConfig = {
secretId: string;
defaultSignName: string;
templates: Record<string, {
signName?: string;
code: string;
}>;
};
export type Config = {
Account?: {
ali?: AliCloudConfig[];
tencent?: TencentCloudConfig[];
qiniu?: QiniuCloudConfig[];
amap?: AmapCloudConfig[];
},
Cos?: {
qiniu?: QiniuCosConfig;
};
Live?: {
qiniu?: QiniuLiveConfig;
},
Map?: {
amap?: {
webApiKey: string;
};
};
UserEntityGrant?: {
lifetimeLength: number; // 授权的过期时间ms
};
Sms?: {
ali?: AliSmsConfig[];
tencent?: TencentSmsConfig[];
}
};
export type Origin = 'ali' | 'tencent' | 'qiniu' | 'amap';
export type Service = 'Map' | 'Cos' | 'Live' | 'Sms';

View File

@ -1,5 +1,5 @@
import { OpSchema as ExtraFile } from '../general-app-domain/ExtraFile/Schema';
import { SystemConfig } from '../general-app-domain/System/Schema';
import { Config } from '../types/Config';
export function composeFileUrl(
extraFile: Pick<
@ -13,7 +13,7 @@ export function composeFileUrl(
| 'extension'
| 'entity'
>,
systemConfig?: SystemConfig
config?: Config
) {
const { type, bucket, filename, origin, extra1, objectId, extension, entity } =
extraFile;
@ -27,9 +27,9 @@ export function composeFileUrl(
}
return extra1;
}
if (systemConfig && systemConfig.Cos) {
if (config && config.Cos) {
const { domain, protocol } =
systemConfig.Cos[origin as keyof typeof systemConfig.Cos]!;
config.Cos[origin as keyof typeof config.Cos]!;
let protocol2 = protocol;
if (protocol instanceof Array) {
// protocol存在https 说明域名有证书

View File

@ -0,0 +1,79 @@
import { assert } from 'console';
import { OakDataException } from 'oak-domain/lib/types/Exception';
import { AmapSDK, QiniuSDK } from 'oak-external-sdk';
import { RuntimeContext } from '../context/RuntimeContext';
import { EntityDict } from '../general-app-domain';
import { AliCloudConfig, AmapCloudConfig, Config, Origin, QiniuCloudConfig, Service, TencentCloudConfig } from '../types/Config';
export async function getConfig<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(context: Cxt, service: Service, origin: Origin) {
const { rowStore } = context;
const systemId = await context.getSystemId();
assert(systemId);
const { result: [system] } = await rowStore.select('system', {
data: {
id: 1,
config: 1,
platform: {
id: 1,
config: 1,
}
},
filter: {
id: systemId!
}
}, context, {
dontCollect: true,
});
const { config: systemConfig, platform } = system as EntityDict['system']['Schema'];
let originConfig = systemConfig[service] && (systemConfig[service] as any)[origin];
let originCloudAccounts = originConfig && systemConfig.Account && systemConfig.Account[origin];
if (!originConfig) {
const { config: platformConfig } = platform ;
originConfig = platformConfig[service] && (platformConfig[service] as any)[origin];
originCloudAccounts = originConfig && platformConfig.Account && platformConfig.Account[origin];
}
if (!originConfig) {
throw new OakDataException(`调用的服务${service}${origin}找不到相应的配置,请联系管理员`);
}
switch (origin) {
case 'ali': {
const aliAccount = (originCloudAccounts as AliCloudConfig[]).find(
ele => ele.accessKeyId === originConfig.accessKeyId
);
assert(aliAccount, `调用的服务${service}${origin}找不到相应的云平台帐号,请联系管理员`);
throw new Error('阿里云的external SDK还未实现');
}
case 'tencent': {
const tencentAccount = (originCloudAccounts as TencentCloudConfig[]).find(
ele => ele.secretId === originConfig.secretId
);
assert(tencentAccount, `调用的服务${service}${origin}找不到相应的云平台帐号,请联系管理员`);
throw new Error('腾讯云的external SDK还未实现');
}
case 'qiniu': {
const qiniuAccount = (originCloudAccounts as QiniuCloudConfig[]).find(
ele => ele.accessKey === originConfig.accessKey
);
assert(qiniuAccount, `调用的服务${service}${origin}找不到相应的云平台帐号,请联系管理员`);
const qiniuInstance = QiniuSDK.getInstance(qiniuAccount!.accessKey, qiniuAccount!.secretKey);
return {
instance: qiniuInstance,
config: originConfig,
};
}
default: {
assert (origin === 'amap');
const amapAccount = (originCloudAccounts as AmapCloudConfig[]).find(
ele => ele.webApiKey === originConfig.webApiKey
);
assert(amapAccount, `调用的服务${service}${origin}找不到相应的云平台帐号,请联系管理员`);
const amapInstance = AmapSDK.getInstance(amapAccount!.webApiKey);
return {
instance: amapInstance,
config: originConfig,
};
}
}
}

96
src/utils/livestream.ts Normal file
View File

@ -0,0 +1,96 @@
import { RuntimeContext } from '../context/RuntimeContext';
import { EntityDict } from '../general-app-domain';
import { Schema as Livestream } from '../general-app-domain/Livestream/Schema';
import { Origin, QiniuLiveConfig } from '../types/Config';
import { getConfig } from './getContextConfig';
import { QiniuCloudInstance } from 'oak-external-sdk';
import assert from 'assert';
/**
*
* @param streamTitle
* @param expireAt
* @param context context
* @returns Livestream
*/
export async function getLivestream<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(
params: {
origin: Origin;
streamTitle: string,
expireAt: number,
},
context: Cxt
): Promise<Pick<Livestream,
| 'streamTitle'
| 'hub'
| 'rtmpPushUrl'
| 'rtmpPlayUrl'
| 'pcPushUrl'
| 'streamKey'
| 'expireAt'>
> {
const { streamTitle, expireAt, origin } = params;
// 获取七牛直播云信息
const {
instance,
config,
} = await getConfig<ED, Cxt>(context, 'Live', origin);
assert(origin === 'qiniu');
const { hub, liveHost, publishDomain, playDomain, playKey, publishKey } = config as QiniuLiveConfig;
return (<QiniuCloudInstance>instance).getLiveStream(hub, 'POST', streamTitle, liveHost, publishDomain, playDomain, publishKey, playKey, expireAt);
}
// 获取推拉流地址
/**
*
* @param streamTitle
* @param expireAt
* @param context context
* @returns livestream对象
*/
export async function getStreamObj<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(
params: {
origin: Origin;
streamTitle: string;
expireAt: number;
},
context: Cxt
): Promise<Pick<Livestream,
| 'streamTitle'
| 'hub'
| 'rtmpPushUrl'
| 'rtmpPlayUrl'
| 'pcPushUrl'
| 'streamKey'
| 'expireAt'>
> {
const { streamTitle, expireAt, origin } = params;
const {
instance,
config,
} = await getConfig<ED, Cxt>(context, 'Live', origin);
assert(origin === 'qiniu');
const { publishDomain: publishDomain, publishKey: publishKey, playDomain, playKey, hub } = config as QiniuLiveConfig;
return (<QiniuCloudInstance>instance).getStreamObj(publishDomain, playDomain, hub, publishKey, playKey, streamTitle, expireAt);
}
// 生成直播回放
export async function getPlayBackUrl<ED extends EntityDict, Cxt extends RuntimeContext<ED>>(
params: {
origin: Origin;
streamTitle: string;
start: number;
end: number;
},
context: Cxt
) {
const { streamTitle, start, end, origin } = params;
// 获取七牛直播云信息
const {
config,
instance
} = await getConfig<ED, Cxt>(context, 'Live', origin);
const { hub, playBackDomain, liveHost } = config as QiniuLiveConfig;
return (<QiniuCloudInstance>instance).getPlayBackUrl(hub, playBackDomain, streamTitle, start, end, 'POST', liveHost);
}