init: the first public commit for AFFiNE

This commit is contained in:
DarkSky
2022-07-22 15:49:21 +08:00
commit e3e3741393
1451 changed files with 108124 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
{
"presets": [
[
"@nrwl/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
+18
View File
@@ -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": {}
}
]
}
+128
View File
@@ -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);
};
```
+9
View File
@@ -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',
};
+13
View File
@@ -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"
}
}
+45
View File
@@ -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 credentialsused 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',
},
};
+21
View File
@@ -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;
+27
View File
@@ -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"
]
}