Commit 7ff2e7ab authored by nanahira's avatar nanahira

file datasource & sync accounts

parent 188aa128
...@@ -3,9 +3,15 @@ port: 3000 ...@@ -3,9 +3,15 @@ port: 3000
redisUrl: '' redisUrl: ''
token: '' token: ''
accounts: accounts:
type: 'static',
config:
- email: 'test@example.com' - email: 'test@example.com'
password: 'wwww' password: 'wwww'
pro: false pro: false
loginType: 'default' loginType: 'default'
proxies: [] proxies:
apiKeys: [] type: 'static'
\ No newline at end of file config: []
apiKeys:
type: 'static'
config: []
\ No newline at end of file
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
"@nestjs/config": "^2.3.1", "@nestjs/config": "^2.3.1",
"@nestjs/core": "^9.0.0", "@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0", "@nestjs/platform-express": "^9.0.0",
"@nestjs/schedule": "^2.2.0",
"@nestjs/swagger": "^6.2.1", "@nestjs/swagger": "^6.2.1",
"aragami": "^1.1.3", "aragami": "^1.1.3",
"axios": "^1.3.3", "axios": "^1.3.3",
...@@ -1647,6 +1648,20 @@ ...@@ -1647,6 +1648,20 @@
"@nestjs/core": "^9.0.0" "@nestjs/core": "^9.0.0"
} }
}, },
"node_modules/@nestjs/schedule": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-2.2.0.tgz",
"integrity": "sha512-wrDnUONTxBkD6lTWh9ecYk/kvJTbA3PylotjBoRsECmcS1SNvgInFXuL38UnHiFnXM3CHSFnzRLB259Bc1mOdQ==",
"dependencies": {
"cron": "2.2.0",
"uuid": "9.0.0"
},
"peerDependencies": {
"@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0",
"@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0",
"reflect-metadata": "^0.1.12"
}
},
"node_modules/@nestjs/schematics": { "node_modules/@nestjs/schematics": {
"version": "9.0.4", "version": "9.0.4",
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.4.tgz", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.4.tgz",
...@@ -3658,6 +3673,14 @@ ...@@ -3658,6 +3673,14 @@
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true "dev": true
}, },
"node_modules/cron": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cron/-/cron-2.2.0.tgz",
"integrity": "sha512-GPiI3OgMv83XRtEUc2gUdaLvJhO3XbLN288layOBkDTupg0RK5IECNGpkykIMHg+muVR2bxt29b0xvCAcBrjYQ==",
"dependencies": {
"luxon": "^3.2.1"
}
},
"node_modules/cross-fetch": { "node_modules/cross-fetch": {
"version": "3.1.5", "version": "3.1.5",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
...@@ -6646,6 +6669,14 @@ ...@@ -6646,6 +6669,14 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"node_modules/luxon": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz",
"integrity": "sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg==",
"engines": {
"node": ">=12"
}
},
"node_modules/macos-release": { "node_modules/macos-release": {
"version": "2.5.0", "version": "2.5.0",
"resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz",
...@@ -11637,6 +11668,15 @@ ...@@ -11637,6 +11668,15 @@
"tslib": "2.5.0" "tslib": "2.5.0"
} }
}, },
"@nestjs/schedule": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-2.2.0.tgz",
"integrity": "sha512-wrDnUONTxBkD6lTWh9ecYk/kvJTbA3PylotjBoRsECmcS1SNvgInFXuL38UnHiFnXM3CHSFnzRLB259Bc1mOdQ==",
"requires": {
"cron": "2.2.0",
"uuid": "9.0.0"
}
},
"@nestjs/schematics": { "@nestjs/schematics": {
"version": "9.0.4", "version": "9.0.4",
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.4.tgz", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.4.tgz",
...@@ -13184,6 +13224,14 @@ ...@@ -13184,6 +13224,14 @@
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true "dev": true
}, },
"cron": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cron/-/cron-2.2.0.tgz",
"integrity": "sha512-GPiI3OgMv83XRtEUc2gUdaLvJhO3XbLN288layOBkDTupg0RK5IECNGpkykIMHg+muVR2bxt29b0xvCAcBrjYQ==",
"requires": {
"luxon": "^3.2.1"
}
},
"cross-fetch": { "cross-fetch": {
"version": "3.1.5", "version": "3.1.5",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
...@@ -15415,6 +15463,11 @@ ...@@ -15415,6 +15463,11 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"luxon": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz",
"integrity": "sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg=="
},
"macos-release": { "macos-release": {
"version": "2.5.0", "version": "2.5.0",
"resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz",
......
...@@ -10,6 +10,7 @@ import { OpenAIAccount } from '../utility/config'; ...@@ -10,6 +10,7 @@ import { OpenAIAccount } from '../utility/config';
import { AccountState } from './account-state'; import { AccountState } from './account-state';
import { AccountProviderService } from '../account-provider/account-provider.service'; import { AccountProviderService } from '../account-provider/account-provider.service';
import { AccountPoolStatusDto } from './account-pool-status.dto'; import { AccountPoolStatusDto } from './account-pool-status.dto';
import { Interval } from '@nestjs/schedule';
@Injectable() @Injectable()
export class AccountPoolService export class AccountPoolService
...@@ -42,9 +43,11 @@ export class AccountPoolService ...@@ -42,9 +43,11 @@ export class AccountPoolService
proxyServer: proxy, proxyServer: proxy,
}), }),
); );
this.log(`Initializing account ${account.email}`);
const success = await state.init(); const success = await state.init();
if (success) { if (success) {
this.accounts.set(account.email, state); this.accounts.set(account.email, state);
this.log(`Initialized account ${account.email}`);
return true; return true;
} else if (retry) { } else if (retry) {
await Promise.all([ await Promise.all([
...@@ -60,6 +63,25 @@ export class AccountPoolService ...@@ -60,6 +63,25 @@ export class AccountPoolService
private accountInfos: OpenAIAccount[]; private accountInfos: OpenAIAccount[];
@Interval(1000 * 60 * 5)
async syncAccounts() {
this.accountInfos = await this.accountProvider.get();
const accountEmails = new Set(this.accountInfos.map((a) => a.email));
const addedAccounts = this.accountInfos.filter(
(a) => !this.accounts.has(a.email),
);
const removedAccounts = Array.from(this.accounts.keys()).filter(
(a) => !accountEmails.has(a),
);
this.log(
`Sync accounts: ${addedAccounts.length} added, ${removedAccounts.length} removed`,
);
await Promise.all([
...addedAccounts.map((a) => this.addAccount(a)),
...removedAccounts.map((a) => this.removeAccount(a)),
]);
}
async onModuleInit() { async onModuleInit() {
this.ChatGPTAPIBrowserConstructor = ( this.ChatGPTAPIBrowserConstructor = (
await eval("import('chatgpt3')") await eval("import('chatgpt3')")
...@@ -112,26 +134,18 @@ export class AccountPoolService ...@@ -112,26 +134,18 @@ export class AccountPoolService
if (this.accounts.has(account.email)) { if (this.accounts.has(account.email)) {
return false; return false;
} }
const result = await this.initAccount(account, false); return this.initAccount(account, false);
if (result) {
this.accountInfos.push(account);
}
await this.accountProvider.writeBack(this.accountInfos, 'add', account);
return result;
} }
async removeAccount(email: string) { async removeAccount(email: string) {
const index = this.accountInfos.findIndex((a) => a.email === email); const state = this.accounts.get(email);
if (index === -1) { if (!state) {
return false; return false;
} }
const [account] = this.accountInfos.splice(index, 1); this.log(`Removing account ${email}`);
const state = this.accounts.get(email);
if (state) {
this.accounts.delete(email); this.accounts.delete(email);
await state.close(); await state.close();
} this.log(`Removed account ${email}`);
await this.accountProvider.writeBack(this.accountInfos, 'remove', account);
return true; return true;
} }
......
...@@ -8,9 +8,4 @@ export class AccountProviderService extends BaseProvider<OpenAIAccount[]> { ...@@ -8,9 +8,4 @@ export class AccountProviderService extends BaseProvider<OpenAIAccount[]> {
constructor(config: ConfigService) { constructor(config: ConfigService) {
super(config, 'accounts'); super(config, 'accounts');
} }
async writeBack(
accounts: OpenAIAccount[],
type: 'add' | 'remove',
account: OpenAIAccount,
) {}
} }
...@@ -12,6 +12,7 @@ import { AccountProviderService } from './account-provider/account-provider.serv ...@@ -12,6 +12,7 @@ import { AccountProviderService } from './account-provider/account-provider.serv
import { ProxyProviderService } from './proxy-provider/proxy-provider.service'; import { ProxyProviderService } from './proxy-provider/proxy-provider.service';
import { KeyProviderService } from './key-provider/key-provider.service'; import { KeyProviderService } from './key-provider/key-provider.service';
import { DavinciService } from './davinci/davinci.service'; import { DavinciService } from './davinci/davinci.service';
import { ScheduleModule } from '@nestjs/schedule';
@Module({ @Module({
imports: [ imports: [
...@@ -35,6 +36,7 @@ import { DavinciService } from './davinci/davinci.service'; ...@@ -35,6 +36,7 @@ import { DavinciService } from './davinci/davinci.service';
return {}; return {};
}, },
}), }),
ScheduleModule.forRoot(),
], ],
controllers: [AppController], controllers: [AppController],
providers: [ providers: [
......
...@@ -6,6 +6,4 @@ export class BaseDatasource<T, K extends keyof DatasourceConfigMap<T>> { ...@@ -6,6 +6,4 @@ export class BaseDatasource<T, K extends keyof DatasourceConfigMap<T>> {
async getData(): Promise<any> { async getData(): Promise<any> {
throw new Error('Not implemented'); throw new Error('Not implemented');
} }
async writeBack(data: T) {}
} }
import { StaticDatasource } from './source/static'; import { StaticDatasource } from './source/static';
import { ApiDatasource } from './source/api'; import { ApiDatasource } from './source/api';
import { BaseDatasource } from './base'; import { BaseDatasource } from './base';
import { FileDatasource } from './source/file';
export interface DatasourceConfigMap<T> { export interface DatasourceConfigMap<T> {
static: T; static: T;
api: { endpoint: string; headers: Record<string, string | number> }; api: { endpoint: string; headers: Record<string, string | number> };
file: { path: string; keys: string[]; separator: string };
} }
export interface DatasourceConfig< export interface DatasourceConfig<
T, T,
...@@ -23,3 +25,4 @@ export const Datasources = new Map< ...@@ -23,3 +25,4 @@ export const Datasources = new Map<
Datasources.set('static', StaticDatasource as any); Datasources.set('static', StaticDatasource as any);
Datasources.set('api', ApiDatasource as any); Datasources.set('api', ApiDatasource as any);
Datasources.set('file', FileDatasource as any);
import { BaseDatasource } from '../base';
import * as fs from 'fs';
export class FileDatasource<T> extends BaseDatasource<T, 'file'> {
async getData() {
const content = await fs.promises.readFile(this.config.path, 'utf-8');
return content
.split('\n')
.map((_line) => {
const line = _line.trim();
if (!line) {
return '';
}
if (!this.config.separator) {
return line;
}
const values = line.split(new RegExp(this.config.separator));
if (!this.config.keys) {
return values;
}
const obj: Record<string, string> = {};
this.config.keys.forEach((key, index) => {
obj[key] = values[index];
});
return obj;
})
.filter((d) => !!d);
}
}
import yaml from 'yaml'; import yaml from 'yaml';
import * as fs from 'fs'; import * as fs from 'fs';
import { DatasourceConfig } from '../datasource/datasource';
export interface OpenAIAccount { export interface OpenAIAccount {
email: string; email: string;
...@@ -11,11 +12,20 @@ export interface OpenAIAccount { ...@@ -11,11 +12,20 @@ export interface OpenAIAccount {
const defaultConfig = { const defaultConfig = {
host: '::', host: '::',
port: 3000, port: 3000,
accounts: [] as OpenAIAccount[], accounts: {
type: 'static',
config: [],
} as DatasourceConfig<OpenAIAccount[]>,
redisUrl: '', redisUrl: '',
token: '', token: '',
proxies: [] as string[], proxies: {
apiKeys: [] as string[], type: 'static',
config: [],
} as DatasourceConfig<string[]>,
apiKeys: {
type: 'static',
config: [],
} as DatasourceConfig<string[]>,
}; };
export type Config = typeof defaultConfig; export type Config = typeof defaultConfig;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment