暂存
This commit is contained in:
@@ -1 +1,3 @@
|
||||
export { sequelize } from './sequelize.ts';
|
||||
|
||||
export * from './minio.ts'
|
||||
14
src/modules/minio.ts
Normal file
14
src/modules/minio.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Client, ClientOptions } from 'minio';
|
||||
import { useConfig } from '@abearxiong/use-config';
|
||||
|
||||
type MinioConfig = {
|
||||
minio: ClientOptions & { bucketName: string };
|
||||
};
|
||||
const config = useConfig<MinioConfig>();
|
||||
|
||||
const { bucketName, ...minioRest } = config.minio;
|
||||
export const minioClient = new Client(minioRest);
|
||||
export { bucketName };
|
||||
if (!minioClient) {
|
||||
throw new Error('Minio client not initialized');
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
import { CustomError } from '@abearxiong/router';
|
||||
import { app } from '../../app.ts';
|
||||
import { ContainerModel, ContainerData, Container } from './models/index.ts';
|
||||
|
||||
import semver from 'semver';
|
||||
const list = app.route({
|
||||
path: 'container',
|
||||
key: 'list',
|
||||
});
|
||||
list.run = async (ctx) => {
|
||||
const list = await ContainerModel.findAll();
|
||||
const list = await ContainerModel.findAll({
|
||||
order: [['updatedAt', 'DESC']],
|
||||
});
|
||||
ctx.body = list;
|
||||
return ctx;
|
||||
};
|
||||
@@ -91,3 +93,49 @@ deleteRoute.run = async (ctx) => {
|
||||
return ctx;
|
||||
};
|
||||
deleteRoute.addTo(app);
|
||||
|
||||
const publish = app.route({
|
||||
path: 'container',
|
||||
key: 'publish',
|
||||
});
|
||||
publish.nextRoute = { path: 'resource', key: 'publishContainer' };
|
||||
|
||||
publish
|
||||
.define(async (ctx) => {
|
||||
const { data } = ctx.query;
|
||||
const { id, publish, type = 'patch', beta = false } = data;
|
||||
type PublishType = 'patch' | 'minor' | 'major';
|
||||
if (!id) {
|
||||
throw new CustomError('id is required');
|
||||
}
|
||||
const container = await ContainerModel.findByPk(id);
|
||||
if (!container) {
|
||||
throw new CustomError('container not found');
|
||||
}
|
||||
const { name, description } = publish;
|
||||
const oldPublish = container.publish;
|
||||
let _version = '';
|
||||
if (!oldPublish.version) {
|
||||
if (publish.name) {
|
||||
throw new CustomError('publish name is required');
|
||||
}
|
||||
container.publish = {
|
||||
name,
|
||||
description,
|
||||
version: '0.0.1',
|
||||
};
|
||||
} else {
|
||||
_version = semver.inc(oldPublish.version, type as PublishType, beta ? 'beta' : undefined);
|
||||
container.publish.version = _version;
|
||||
}
|
||||
if (ctx.state) {
|
||||
ctx.state.container = container;
|
||||
} else {
|
||||
ctx.state = {
|
||||
container,
|
||||
};
|
||||
}
|
||||
// 执行下一步操作了
|
||||
ctx.body = 'run ok';
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
@@ -7,16 +7,14 @@ export interface ContainerData {
|
||||
showChild?: boolean;
|
||||
shadowRoot?: boolean;
|
||||
}
|
||||
export type Container = {
|
||||
id?: string;
|
||||
title?: string;
|
||||
export type ContainerPublish = {
|
||||
rid?: string; // resource id
|
||||
name?: string;
|
||||
description?: string;
|
||||
type?: string;
|
||||
code?: string;
|
||||
source?: string;
|
||||
sourceType?: string;
|
||||
data?: ContainerData;
|
||||
version?: string;
|
||||
};
|
||||
export type Container = Partial<InstanceType<typeof ContainerModel>>;
|
||||
|
||||
export class ContainerModel extends Model {
|
||||
declare id: string;
|
||||
declare title: string;
|
||||
@@ -26,6 +24,12 @@ export class ContainerModel extends Model {
|
||||
declare source: string;
|
||||
declare sourceType: string;
|
||||
declare data: ContainerData;
|
||||
declare publish: ContainerPublish;
|
||||
declare uid: string;
|
||||
|
||||
// timestamps
|
||||
public readonly createdAt!: Date;
|
||||
public readonly updatedAt!: Date;
|
||||
}
|
||||
ContainerModel.init(
|
||||
{
|
||||
@@ -63,6 +67,11 @@ ContainerModel.init(
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: {},
|
||||
},
|
||||
publish: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: {},
|
||||
},
|
||||
|
||||
uid: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: true,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import './container/index.ts';
|
||||
|
||||
import './page/index.ts';
|
||||
import './page/index.ts';
|
||||
|
||||
import './resource/index.ts';
|
||||
|
||||
@@ -210,7 +210,7 @@ app
|
||||
path: 'page',
|
||||
key: 'getDeck',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
.define<any>(async (ctx) => {
|
||||
const id = ctx.query.id;
|
||||
if (!id) {
|
||||
throw new CustomError('id is required');
|
||||
|
||||
2
src/routes/resource/index.ts
Normal file
2
src/routes/resource/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import './publish.ts';
|
||||
import './list.ts';
|
||||
1
src/routes/resource/lib/index.ts
Normal file
1
src/routes/resource/lib/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './publish-minio.ts';
|
||||
35
src/routes/resource/lib/publish-minio.ts
Normal file
35
src/routes/resource/lib/publish-minio.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Resource } from '../models/index.ts';
|
||||
import { minioClient, bucketName } from '../../../modules/minio.ts';
|
||||
|
||||
type MinioRes = {
|
||||
etag?: string; // 文件的etag, 用于后续的文件下载
|
||||
versionId?: string;
|
||||
};
|
||||
|
||||
type PublishOptions = {
|
||||
name: string;
|
||||
version: string;
|
||||
code: string;
|
||||
};
|
||||
|
||||
export const publishJsCode = async ({ name, version, code }: PublishOptions) => {
|
||||
// publish to minio
|
||||
const codeBuffer = Buffer.from(code);
|
||||
const codePath = `${name}/${version}/index.js`;
|
||||
try {
|
||||
const res = await minioClient.putObject(bucketName, codePath, codeBuffer, codeBuffer.length, {
|
||||
'Content-Type': 'application/javascript',
|
||||
'Cache-Control': 'max-age=31536000, immutable',
|
||||
});
|
||||
return {
|
||||
code: 200,
|
||||
data: { ...res, path: codePath },
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('publish error', e.message);
|
||||
return {
|
||||
code: 500,
|
||||
message: e.message,
|
||||
};
|
||||
}
|
||||
};
|
||||
72
src/routes/resource/list.ts
Normal file
72
src/routes/resource/list.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { ResourceData, ResourceModel } from './models/index.ts';
|
||||
import { app } from '../../app.ts';
|
||||
import { CustomError } from '@abearxiong/router';
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'resource',
|
||||
key: 'list',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const list = await ResourceModel.findAll({
|
||||
order: [['updatedAt', 'DESC']],
|
||||
});
|
||||
ctx.body = list;
|
||||
return ctx;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'resource',
|
||||
key: 'get',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const id = ctx.query.id;
|
||||
if (!id) {
|
||||
throw new CustomError('id is required');
|
||||
}
|
||||
const rm = await ResourceModel.findByPk(id);
|
||||
if (!rm) {
|
||||
throw new CustomError('resource not found');
|
||||
}
|
||||
ctx.body = rm;
|
||||
return ctx;
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({ path: 'resource', key: 'update' })
|
||||
.define(async (ctx) => {
|
||||
const { data, id, ...rest } = ctx.query.data;
|
||||
if (id) {
|
||||
const resource = await ResourceModel.findByPk(id);
|
||||
if (resource) {
|
||||
const newResource = await resource.update({ data, ...rest });
|
||||
ctx.body = newResource;
|
||||
}
|
||||
} else if (data) {
|
||||
const resource = await ResourceModel.create({ data, ...rest });
|
||||
ctx.body = resource;
|
||||
}
|
||||
})
|
||||
.addTo(app);
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'resource',
|
||||
key: 'delete',
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const id = ctx.query.id;
|
||||
if (!id) {
|
||||
throw new CustomError('id is required');
|
||||
}
|
||||
const resource = await ResourceModel.findByPk(id);
|
||||
if (!resource) {
|
||||
throw new CustomError('resource not found');
|
||||
}
|
||||
await resource.destroy();
|
||||
ctx.body = 'success';
|
||||
})
|
||||
.addTo(app);
|
||||
87
src/routes/resource/models/index.ts
Normal file
87
src/routes/resource/models/index.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { sequelize } from '../../../modules/sequelize.ts';
|
||||
import { DataTypes, Model } from 'sequelize';
|
||||
|
||||
type FileUrlList = {
|
||||
path: string;
|
||||
etag: string;
|
||||
versionId: string;
|
||||
};
|
||||
export interface ResourceData {
|
||||
list: FileUrlList[];
|
||||
lastestVersion: string;
|
||||
updatedAt: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
export const defaultData: ResourceData = {
|
||||
list: [],
|
||||
lastestVersion: '0.0.0',
|
||||
updatedAt: '',
|
||||
};
|
||||
export type Resource = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
source?: string;
|
||||
sourceId?: string;
|
||||
data?: ResourceData;
|
||||
version?: string;
|
||||
uid?: string;
|
||||
};
|
||||
|
||||
export class ResourceModel extends Model {
|
||||
declare id: string;
|
||||
declare name: string;
|
||||
declare description: string;
|
||||
declare source: string;
|
||||
declare sourceId: string;
|
||||
declare data: ResourceData;
|
||||
declare version: string;
|
||||
declare uid: string;
|
||||
}
|
||||
|
||||
ResourceModel.init(
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
comment: 'id',
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING, // 第一次创建之后就不能修改了,因为这个是用来做唯一标识的
|
||||
defaultValue: '',
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
defaultValue: '',
|
||||
},
|
||||
source: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: '',
|
||||
},
|
||||
sourceId: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: '',
|
||||
},
|
||||
version: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: '0.0.0',
|
||||
},
|
||||
data: {
|
||||
type: DataTypes.JSON,
|
||||
defaultValue: {},
|
||||
},
|
||||
uid: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
tableName: 'kv_resource',
|
||||
},
|
||||
);
|
||||
|
||||
ResourceModel.sync({ alter: true, logging: false }).catch((e) => {
|
||||
console.error('ResourceModel sync', e);
|
||||
});
|
||||
69
src/routes/resource/publish.ts
Normal file
69
src/routes/resource/publish.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { defaultData, Resource, ResourceModel } from './models/index.ts';
|
||||
import { ContainerModel } from './../container/models/index.ts';
|
||||
|
||||
import { app } from '../../app.ts';
|
||||
import { Op } from 'sequelize';
|
||||
import { publishJsCode } from './lib/publish-minio.ts';
|
||||
import { CustomError } from '@abearxiong/router';
|
||||
|
||||
app
|
||||
.route({
|
||||
path: 'resource',
|
||||
key: 'publishContainer',
|
||||
idUsePath: true,
|
||||
})
|
||||
.define(async (ctx) => {
|
||||
const container = ctx.state.container as ContainerModel;
|
||||
const publish = container.publish;
|
||||
const code = container.code;
|
||||
let { name, rid, description, version = '0.0.1' } = publish;
|
||||
const where = [];
|
||||
if (rid) {
|
||||
where.push({ id: rid });
|
||||
}
|
||||
if (name) {
|
||||
where.push({ name });
|
||||
}
|
||||
let resource = await ResourceModel.findOne({ where: { [Op.or]: where } });
|
||||
let isCreate = false;
|
||||
if (!resource) {
|
||||
isCreate = true;
|
||||
resource = await ResourceModel.create({
|
||||
name,
|
||||
description,
|
||||
version,
|
||||
source: 'container',
|
||||
sourceId: container.id,
|
||||
data: {
|
||||
...defaultData,
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
});
|
||||
}
|
||||
publish.rid = publish.rid || resource.id;
|
||||
// TODO: check version
|
||||
const res = await publishJsCode({ name, version, code });
|
||||
if (res.code === 200) {
|
||||
await container.update({ publish });
|
||||
const { etag, versionId, path } = res.data;
|
||||
resource.version = version;
|
||||
const newData = {
|
||||
list: [],
|
||||
...resource.data,
|
||||
};
|
||||
newData.list.push({
|
||||
etag,
|
||||
versionId,
|
||||
path,
|
||||
});
|
||||
newData.lastestVersion = version;
|
||||
newData.updatedAt = new Date().toISOString();
|
||||
resource.data = newData;
|
||||
await resource.save();
|
||||
ctx.body = { resource, container, resourceIsNew: isCreate };
|
||||
} else {
|
||||
throw new CustomError(res.message);
|
||||
}
|
||||
// await container.update({ publish });
|
||||
})
|
||||
.addTo(app);
|
||||
Reference in New Issue
Block a user