mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-23 05:25:53 +08:00
init: the first public commit for AFFiNE
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nrwl/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": ["plugin:@nrwl/nx/react", "../../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
## Attachment module documentation
|
||||
|
||||
### describe
|
||||
|
||||
The attachment system currently provides AWS S3 object storage service, using the default IAM account for read and write operations;
|
||||
|
||||
###Instructions for use
|
||||
|
||||
**Initialize attachment system**
|
||||
|
||||
```javascript
|
||||
// Initialize the accessory system, the platorm configuration is as follows, for reference
|
||||
const PLATFORM_CONFIG = {
|
||||
AWSS3: 'awss3', // key replace with uuid
|
||||
ALIYUN: 'ailiyun',
|
||||
};
|
||||
|
||||
// It is recommended not to pass the platform type for initialization, AWS S3 is used by default
|
||||
import { upload, multipartUpload } from '@toeverything/components/attachment';
|
||||
```
|
||||
|
||||
**Instance methods**
|
||||
|
||||
_In the function description, if "batch" is not marked, the default is a single file operation; _
|
||||
|
||||
- upload: file upload
|
||||
|
||||
- Type definition
|
||||
|
||||
```typescript
|
||||
type upload = (file: File) => Promise<Error | Url>;
|
||||
```
|
||||
|
||||
- Usage example
|
||||
|
||||
```javascript
|
||||
const onChange = async info => {
|
||||
const { file } = info;
|
||||
const url = await upload(file.originFileObj);
|
||||
console.log(url);
|
||||
};
|
||||
|
||||
// or
|
||||
const onChange = async info => {
|
||||
const { file } = info;
|
||||
const [err, url] = await toAsync(upload(file.originFileObj));
|
||||
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(url);
|
||||
};
|
||||
```
|
||||
|
||||
- multipartUpload: file upload in parts
|
||||
|
||||
- Type definition
|
||||
|
||||
```typescript
|
||||
type multipartUpload = (file: File) => Promise<Error | Url>;
|
||||
```
|
||||
|
||||
- Usage example
|
||||
|
||||
```javascript
|
||||
const onChange = async info => {
|
||||
const { file } = info;
|
||||
const url = await multipartUpload(file.originFileObj);
|
||||
console.log(url);
|
||||
};
|
||||
```
|
||||
|
||||
- batchUpload: **batch** upload
|
||||
|
||||
- Type definition
|
||||
|
||||
```typescript
|
||||
interface data {
|
||||
success: Array<{ name: string; url: string }>;
|
||||
failed: Array<{ name: string; reason: string }>;
|
||||
}
|
||||
|
||||
type batchUpload = (fileList: File[]) => Promise<Error | data>;
|
||||
```
|
||||
|
||||
- Usage example
|
||||
|
||||
```javascript
|
||||
const onChange = async info => {
|
||||
const fileList = info.fileList.map(file => file.originFileObj);
|
||||
const { success } = await batchUpload(fileList);
|
||||
const urls = success.map(item => item.url);
|
||||
};
|
||||
```
|
||||
|
||||
- download: file download
|
||||
|
||||
- Type definition
|
||||
|
||||
```typescript
|
||||
type download = (fileName: string) => Promise<Error | true>;
|
||||
```
|
||||
|
||||
- Usage example
|
||||
|
||||
```javascript
|
||||
const onChange = async fileName => {
|
||||
// browser download
|
||||
await download(fileName);
|
||||
};
|
||||
```
|
||||
|
||||
- recovery: file deletion
|
||||
|
||||
- Type definition
|
||||
|
||||
```typescript
|
||||
type recovery = (fileName: string) => Promise<Error | true>;
|
||||
```
|
||||
|
||||
- Usage example
|
||||
|
||||
```javascript
|
||||
const onChange = async fileName => {
|
||||
await recovery(fileName);
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
displayName: 'components-attachment',
|
||||
preset: '../../../jest.preset.js',
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../../coverage/libs/components/attachment',
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@toeverything/datasource/remote-kv",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"aws-sdk": "^2.1167.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"util": "^0.12.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/file-saver": "^2.0.5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/datasource/remote-kv/src",
|
||||
"projectType": "library",
|
||||
"tags": ["datasource:remote-kv"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nrwl/web:rollup",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/libs/datasource/remote-kv",
|
||||
"tsConfig": "libs/datasource/remote-kv/tsconfig.lib.json",
|
||||
"project": "libs/datasource/remote-kv/package.json",
|
||||
"entryFile": "libs/datasource/remote-kv/src/index.ts",
|
||||
"external": ["react/jsx-runtime"],
|
||||
"rollupConfig": "@nrwl/react/plugins/bundle-rollup",
|
||||
"compiler": "babel",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "libs/datasource/remote-kv/README.md",
|
||||
"input": ".",
|
||||
"output": "."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"libs/datasource/remote-kv/**/*.{ts,tsx,js,jsx}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"outputs": ["coverage/libs/datasource/remote-kv"],
|
||||
"options": {
|
||||
"jestConfig": "libs/datasource/remote-kv/jest.config.js",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
|
||||
import * as AWS from 'aws-sdk';
|
||||
import type { Credentials } from 'aws-sdk';
|
||||
import type { ManagedUpload } from 'aws-sdk/lib/s3/managed_upload';
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
import {
|
||||
bucketName,
|
||||
bucketRegion,
|
||||
folderName,
|
||||
identityPoolId,
|
||||
keyCreator,
|
||||
} from './aws-config';
|
||||
import toAsync from '../utils/to-async';
|
||||
import { MESSAGE_INFO } from '../constant/message-info';
|
||||
|
||||
export default class AwsAttachment {
|
||||
readonly #s3;
|
||||
|
||||
constructor(token?: string) {
|
||||
if (token) {
|
||||
AWS.config.region = bucketRegion;
|
||||
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
|
||||
IdentityPoolId: identityPoolId,
|
||||
WebIdentityToken: token,
|
||||
});
|
||||
|
||||
/**
|
||||
* get credentials,used when uploading from the server
|
||||
*/
|
||||
(AWS.config.credentials as Credentials).get(err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// console.log('authing:', AWS.config.credentials);
|
||||
});
|
||||
|
||||
this.#s3 = new AWS.S3({
|
||||
apiVersion: '2006-03-01',
|
||||
params: { Bucket: bucketName },
|
||||
});
|
||||
// this.#s3 = new AWS.S3Client({
|
||||
// region: bucketRegion,
|
||||
// credentials: fromCognitoIdentityPool({
|
||||
// clientConfig: { region: bucketRegion }, // Configure the underlying CognitoIdentityClient.
|
||||
// identityPoolId: identityPoolId,
|
||||
// logins: {
|
||||
// 'affine.us.authing.co/oidc/': token
|
||||
// }
|
||||
// })
|
||||
// });
|
||||
} else {
|
||||
throw new Error(MESSAGE_INFO.NO_AUTH.FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* upload
|
||||
* @param file
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
upload = (file: File) => {
|
||||
const params = {
|
||||
Bucket: bucketName,
|
||||
Key: `${encodeURIComponent(folderName)}/${file.name}`,
|
||||
Body: file,
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.#s3.upload(
|
||||
params,
|
||||
(err: Error, data: ManagedUpload.SendData) => {
|
||||
if (err) {
|
||||
return reject(`${MESSAGE_INFO.UPLOAD.FAILURE}: ${err}`);
|
||||
}
|
||||
|
||||
resolve(data.Location);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// multipart upload
|
||||
multipartUpload = (file: File): Promise<unknown> => {
|
||||
const upload = new AWS.S3.ManagedUpload({
|
||||
params: {
|
||||
Bucket: bucketName,
|
||||
Key: keyCreator(file.name),
|
||||
Body: file,
|
||||
},
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
upload.promise().then(
|
||||
data => {
|
||||
resolve(data.Location);
|
||||
},
|
||||
(err: Error) => {
|
||||
reject(`${MESSAGE_INFO.UPLOAD.FAILURE}: ${err}`);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// file upload creator
|
||||
uploadPromiseCreator = (fileList: File[]): Promise<string>[] => {
|
||||
return fileList.map(file => {
|
||||
const params = {
|
||||
Bucket: bucketName,
|
||||
Key: keyCreator(file.name),
|
||||
Body: file,
|
||||
};
|
||||
|
||||
return (() =>
|
||||
new Promise((resolve, reject) => {
|
||||
this.#s3.upload(
|
||||
params,
|
||||
(err: Error, data: ManagedUpload.SendData) => {
|
||||
if (err) {
|
||||
reject(
|
||||
`${MESSAGE_INFO.UPLOAD.FAILURE}: ${err}`
|
||||
);
|
||||
}
|
||||
|
||||
resolve(data.Location);
|
||||
}
|
||||
);
|
||||
}))();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* bath upload
|
||||
* @param fileList
|
||||
* @returns {Promise<Array<{}>>}
|
||||
*/
|
||||
batchUpload = async (fileList: File[]) => {
|
||||
/**
|
||||
* file upload creator
|
||||
* @param fileList
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
const handleList = this.uploadPromiseCreator(fileList);
|
||||
const [err, data_source] = await toAsync(
|
||||
Promise.allSettled(handleList)
|
||||
);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (err) {
|
||||
return reject(`${MESSAGE_INFO.UPLOAD.FAILURE}: ${err}`);
|
||||
}
|
||||
|
||||
const result = data_source.reduce(
|
||||
(
|
||||
{
|
||||
success,
|
||||
failed,
|
||||
}: {
|
||||
success: Array<{ name: string; url: string }>;
|
||||
failed: Array<{ name: string; reason: string }>;
|
||||
},
|
||||
current: Record<string, string>,
|
||||
index: number
|
||||
) => {
|
||||
const { name } = fileList[index];
|
||||
const { value, reason } = current;
|
||||
|
||||
value
|
||||
? success.push({ name, url: value })
|
||||
: failed.push({ name, reason });
|
||||
|
||||
return { success, failed };
|
||||
},
|
||||
{ success: [], failed: [] }
|
||||
);
|
||||
|
||||
resolve(result);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* get file object from AWS
|
||||
* @param fileName
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
getBuffData = (fileName: string) => {
|
||||
const params = {
|
||||
Bucket: bucketName,
|
||||
Key: keyCreator(fileName),
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.#s3.getObject(params, (err: Error, data) => {
|
||||
if (err) {
|
||||
return reject(`${MESSAGE_INFO.GET.FAILURE}: ${err}`);
|
||||
}
|
||||
|
||||
resolve(data.Body);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* download file form AWS
|
||||
* @param fileName
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
download = async (fileName: string) => {
|
||||
const [err, buff] = await toAsync(this.getBuffData(fileName));
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (err) {
|
||||
return reject(`${MESSAGE_INFO.DOWNLOAD.FAILURE}: ${err}`);
|
||||
}
|
||||
|
||||
// Browser download
|
||||
saveAs(new Blob([buff], { type: 'arraybuffer' }), fileName);
|
||||
|
||||
resolve(true);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* check object is exist on AWS
|
||||
* @param fileName
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
exist = (fileName: string) => {
|
||||
const params = {
|
||||
Bucket: bucketName,
|
||||
Key: keyCreator(fileName),
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.#s3.headObject(params, (err: Error) => {
|
||||
if (err && err.name === 'NotFound') {
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* delete file on AWS
|
||||
* @param fileName
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
recovery = (fileName: string) => {
|
||||
const params = {
|
||||
Bucket: bucketName,
|
||||
Key: keyCreator(fileName),
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.#s3.deleteObject(params, (err: Error) => {
|
||||
if (err) {
|
||||
return reject(`${MESSAGE_INFO.DELETE.FAILURE} ${err}`);
|
||||
}
|
||||
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
const bucketRegion = 'us-east-1';
|
||||
const identityPoolId = 'us-east-1:a03b5510-f924-4f55-94f6-ed7fa9aeebca';
|
||||
const bucketName = 'affine-uploads';
|
||||
const folderName = 'mvp';
|
||||
|
||||
export const keyCreator = (fileName: string) =>
|
||||
`${encodeURIComponent(folderName)}/${fileName}`;
|
||||
|
||||
export { bucketRegion, identityPoolId, bucketName, folderName };
|
||||
@@ -0,0 +1,22 @@
|
||||
export const MESSAGE_INFO = {
|
||||
UPLOAD: {
|
||||
SUCCESS: '',
|
||||
FAILURE: 'Failed to upload file',
|
||||
},
|
||||
DOWNLOAD: {
|
||||
SUCCESS: '',
|
||||
FAILURE: 'Failed to download file',
|
||||
},
|
||||
GET: {
|
||||
SUCCESS: '',
|
||||
FAILURE: 'Failed to get object',
|
||||
},
|
||||
DELETE: {
|
||||
SUCCESS: '',
|
||||
FAILURE: 'Failed to delete file',
|
||||
},
|
||||
NO_AUTH: {
|
||||
SUCCESS: '',
|
||||
FAILURE: 'Failed to auth',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import AWSAttachment from './aws/aws-attachment';
|
||||
|
||||
const PLATFORM_CONFIG = {
|
||||
AWS_S3: 'aws_s3', // key replace with uuid
|
||||
};
|
||||
|
||||
const PLATFORM = {
|
||||
[PLATFORM_CONFIG.AWS_S3]: AWSAttachment,
|
||||
};
|
||||
|
||||
export class RemoteKvService {
|
||||
readonly #case;
|
||||
|
||||
constructor(token?: string, platform = PLATFORM_CONFIG.AWS_S3) {
|
||||
this.#case = new PLATFORM[platform](token);
|
||||
}
|
||||
|
||||
get instance() {
|
||||
return this.#case;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
const toAsync = (promise: Promise<unknown>) =>
|
||||
promise.then(data => [null, data]).catch(err => [err]);
|
||||
|
||||
export default toAsync;
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"types": ["node"]
|
||||
},
|
||||
"files": [
|
||||
"../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
|
||||
"../../../node_modules/@nrwl/react/typings/image.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx"
|
||||
],
|
||||
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.jsx",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user