mirror of
https://github.com/zkitter/zkitterd.git
synced 2026-01-09 13:47:56 -05:00
chore: format
This commit is contained in:
180
package.json
180
package.json
@@ -1,92 +1,92 @@
|
||||
{
|
||||
"name": "autism-node",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev-server": "nodemon ./build/server.js",
|
||||
"watch-server": "webpack --config webpack.server.config.js --watch",
|
||||
"build-server": "webpack --config webpack.server.config.js",
|
||||
"dev": "NODE_ENV=development concurrently --kill-others-on-fail npm:watch-server npm:dev-server",
|
||||
"build": "NODE_ENV=production npm run build-server",
|
||||
"start": "NODE_ENV=production npm run build && node ./build/server.js",
|
||||
"test": "NODE_ENV=test TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' tape -r ts-node/register ./src/**/*.test.ts ./src/**/**/*.test.ts | tap-spec",
|
||||
"test:coverage": "nyc npm run test",
|
||||
"format:check": "prettier --check 'src/**/*.ts'",
|
||||
"format:write": "prettier --write 'src/**/*.ts'",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@ensdomains/ensjs": "^2.0.1",
|
||||
"@interep/reputation": "^0.4.0",
|
||||
"@zk-kit/identity": "^1.4.1",
|
||||
"@zk-kit/protocols": "^1.11.1",
|
||||
"async-mutex": "^0.3.1",
|
||||
"bn.js": "^5.2.0",
|
||||
"body-parser": "^1.19.0",
|
||||
"botometer": "^1.0.12",
|
||||
"connect-session-sequelize": "^7.1.2",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.0.1",
|
||||
"elliptic": "^6.5.4",
|
||||
"ethereumjs-util": "^7.1.2",
|
||||
"express": "^4.17.1",
|
||||
"express-fileupload": "^1.4.0",
|
||||
"express-session": "^1.17.2",
|
||||
"gun": "0.2020.1232",
|
||||
"http-terminator": "^3.2.0",
|
||||
"ipfs-http-client": "56.x",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"link-preview-js": "^2.1.8",
|
||||
"link-preview-node": "^1.0.7",
|
||||
"lru-cache": "^6.0.0",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
"pg": "^8.7.1",
|
||||
"semaphore-lib": "git+https://github.com/akinovak/semaphore-lib.git#dev",
|
||||
"sequelize": "6.6.5",
|
||||
"sqlite3": "^5.0.2",
|
||||
"web3": "^1.3.5",
|
||||
"web3.storage": "^4.3.0",
|
||||
"winston": "^3.3.3",
|
||||
"zk-chat-server": "^1.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||
"@types/cors": "^2.8.10",
|
||||
"@types/elliptic": "^6.4.14",
|
||||
"@types/express": "^4.17.11",
|
||||
"@types/express-fileupload": "^1.2.2",
|
||||
"@types/express-session": "^1.17.4",
|
||||
"@types/jsonwebtoken": "^8.5.6",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "^14.14.37",
|
||||
"@types/sinon": "^10.0.11",
|
||||
"@types/tape": "^4.13.2",
|
||||
"concurrently": "^6.2.0",
|
||||
"csv": "^6.0.5",
|
||||
"husky": "^8.0.0",
|
||||
"node-loader": "^2.0.0",
|
||||
"nodemon": "^2.0.12",
|
||||
"nyc": "^15.1.0",
|
||||
"prettier": "^2.7.1",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"sinon": "^13.0.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"tap-diff": "^0.1.1",
|
||||
"tap-spec": "^5.0.0",
|
||||
"tape": "^5.5.2",
|
||||
"ts-loader": "^6.2.1",
|
||||
"ts-node": "^10.7.0",
|
||||
"typescript": "^4.3.5",
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-node-externals": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 <17"
|
||||
}
|
||||
"name": "autism-node",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev-server": "nodemon ./build/server.js",
|
||||
"watch-server": "webpack --config webpack.server.config.js --watch",
|
||||
"build-server": "webpack --config webpack.server.config.js",
|
||||
"dev": "NODE_ENV=development concurrently --kill-others-on-fail npm:watch-server npm:dev-server",
|
||||
"build": "NODE_ENV=production npm run build-server",
|
||||
"start": "NODE_ENV=production npm run build && node ./build/server.js",
|
||||
"test": "NODE_ENV=test TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' tape -r ts-node/register ./src/**/*.test.ts ./src/**/**/*.test.ts | tap-spec",
|
||||
"test:coverage": "nyc npm run test",
|
||||
"format:check": "prettier --check 'src/**/*.ts'",
|
||||
"format:write": "prettier --write 'src/**/*.ts'",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@ensdomains/ensjs": "^2.0.1",
|
||||
"@interep/reputation": "^0.4.0",
|
||||
"@zk-kit/identity": "^1.4.1",
|
||||
"@zk-kit/protocols": "^1.11.1",
|
||||
"async-mutex": "^0.3.1",
|
||||
"bn.js": "^5.2.0",
|
||||
"body-parser": "^1.19.0",
|
||||
"botometer": "^1.0.12",
|
||||
"connect-session-sequelize": "^7.1.2",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.0.1",
|
||||
"elliptic": "^6.5.4",
|
||||
"ethereumjs-util": "^7.1.2",
|
||||
"express": "^4.17.1",
|
||||
"express-fileupload": "^1.4.0",
|
||||
"express-session": "^1.17.2",
|
||||
"gun": "0.2020.1232",
|
||||
"http-terminator": "^3.2.0",
|
||||
"ipfs-http-client": "56.x",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"link-preview-js": "^2.1.8",
|
||||
"link-preview-node": "^1.0.7",
|
||||
"lru-cache": "^6.0.0",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
"pg": "^8.7.1",
|
||||
"semaphore-lib": "git+https://github.com/akinovak/semaphore-lib.git#dev",
|
||||
"sequelize": "6.6.5",
|
||||
"sqlite3": "^5.0.2",
|
||||
"web3": "^1.3.5",
|
||||
"web3.storage": "^4.3.0",
|
||||
"winston": "^3.3.3",
|
||||
"zk-chat-server": "^1.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||
"@types/cors": "^2.8.10",
|
||||
"@types/elliptic": "^6.4.14",
|
||||
"@types/express": "^4.17.11",
|
||||
"@types/express-fileupload": "^1.2.2",
|
||||
"@types/express-session": "^1.17.4",
|
||||
"@types/jsonwebtoken": "^8.5.6",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "^14.14.37",
|
||||
"@types/sinon": "^10.0.11",
|
||||
"@types/tape": "^4.13.2",
|
||||
"concurrently": "^6.2.0",
|
||||
"csv": "^6.0.5",
|
||||
"husky": "^8.0.0",
|
||||
"node-loader": "^2.0.0",
|
||||
"nodemon": "^2.0.12",
|
||||
"nyc": "^15.1.0",
|
||||
"prettier": "^2.7.1",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"sinon": "^13.0.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"tap-diff": "^0.1.1",
|
||||
"tap-spec": "^5.0.0",
|
||||
"tape": "^5.5.2",
|
||||
"ts-loader": "^6.2.1",
|
||||
"ts-node": "^10.7.0",
|
||||
"typescript": "^4.3.5",
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-node-externals": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 <17"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module.exports = {
|
||||
trailingComma: 'es5',
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
printWidth: 100,
|
||||
arrowParens: 'avoid',
|
||||
tabWidth: 4,
|
||||
trailingComma: 'es5',
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
printWidth: 100,
|
||||
arrowParens: 'avoid',
|
||||
tabWidth: 2,
|
||||
};
|
||||
|
||||
34
src/index.ts
34
src/index.ts
@@ -13,21 +13,21 @@ import MerkleService from './services/merkle';
|
||||
import { ReputationService } from './services/reputation';
|
||||
|
||||
(async function initApp() {
|
||||
try {
|
||||
const main = new MainService();
|
||||
main.add('db', new DBService());
|
||||
main.add('merkle', new MerkleService());
|
||||
main.add('interrep', new InterrepService());
|
||||
main.add('zkchat', new ZKChatService());
|
||||
main.add('ens', new ENSService());
|
||||
main.add('arbitrum', new ArbitrumService());
|
||||
main.add('gun', new GunService());
|
||||
main.add('ipfs', new IPFSService());
|
||||
main.add('http', new HttpService());
|
||||
main.add('reputation', new ReputationService());
|
||||
await main.start();
|
||||
} catch (e) {
|
||||
logger.error(e.message, { stack: e.stack });
|
||||
throw e;
|
||||
}
|
||||
try {
|
||||
const main = new MainService();
|
||||
main.add('db', new DBService());
|
||||
main.add('merkle', new MerkleService());
|
||||
main.add('interrep', new InterrepService());
|
||||
main.add('zkchat', new ZKChatService());
|
||||
main.add('ens', new ENSService());
|
||||
main.add('arbitrum', new ArbitrumService());
|
||||
main.add('gun', new GunService());
|
||||
main.add('ipfs', new IPFSService());
|
||||
main.add('http', new HttpService());
|
||||
main.add('reputation', new ReputationService());
|
||||
await main.start();
|
||||
} catch (e) {
|
||||
logger.error(e.message, { stack: e.stack });
|
||||
throw e;
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -4,111 +4,111 @@ import { Mutex } from 'async-mutex';
|
||||
const mutex = new Mutex();
|
||||
|
||||
type AppModel = {
|
||||
lastENSBlockScanned: number;
|
||||
lastInterrepBlockScanned: number;
|
||||
lastArbitrumBlockScanned: number;
|
||||
lastGroup42BlockScanned: number;
|
||||
lastENSBlockScanned: number;
|
||||
lastInterrepBlockScanned: number;
|
||||
lastArbitrumBlockScanned: number;
|
||||
lastGroup42BlockScanned: number;
|
||||
};
|
||||
|
||||
const app = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'app',
|
||||
{
|
||||
lastENSBlockScanned: {
|
||||
type: BIGINT,
|
||||
},
|
||||
lastInterrepBlockScanned: {
|
||||
type: BIGINT,
|
||||
},
|
||||
lastArbitrumBlockScanned: {
|
||||
type: BIGINT,
|
||||
},
|
||||
lastGroup42BlockScanned: {
|
||||
type: BIGINT,
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'app',
|
||||
{
|
||||
lastENSBlockScanned: {
|
||||
type: BIGINT,
|
||||
},
|
||||
lastInterrepBlockScanned: {
|
||||
type: BIGINT,
|
||||
},
|
||||
lastArbitrumBlockScanned: {
|
||||
type: BIGINT,
|
||||
},
|
||||
lastGroup42BlockScanned: {
|
||||
type: BIGINT,
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const read = async (): Promise<AppModel> => {
|
||||
return mutex.runExclusive(async () => {
|
||||
let result = await model.findOne();
|
||||
return result?.toJSON() as AppModel;
|
||||
const read = async (): Promise<AppModel> => {
|
||||
return mutex.runExclusive(async () => {
|
||||
let result = await model.findOne();
|
||||
return result?.toJSON() as AppModel;
|
||||
});
|
||||
};
|
||||
|
||||
const updateLastENSBlock = async (blockHeight: number) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne();
|
||||
|
||||
if (result) {
|
||||
return result.update({
|
||||
lastENSBlockScanned: blockHeight,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const updateLastENSBlock = async (blockHeight: number) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne();
|
||||
return model.create({
|
||||
lastENSBlockScanned: blockHeight,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (result) {
|
||||
return result.update({
|
||||
lastENSBlockScanned: blockHeight,
|
||||
});
|
||||
}
|
||||
const updateLastInterrepBlock = async (blockHeight: number) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne();
|
||||
|
||||
return model.create({
|
||||
lastENSBlockScanned: blockHeight,
|
||||
});
|
||||
if (result) {
|
||||
return result.update({
|
||||
lastInterrepBlockScanned: blockHeight,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const updateLastInterrepBlock = async (blockHeight: number) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne();
|
||||
return model.create({
|
||||
lastInterrepBlockScanned: blockHeight,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (result) {
|
||||
return result.update({
|
||||
lastInterrepBlockScanned: blockHeight,
|
||||
});
|
||||
}
|
||||
const updateLastArbitrumBlock = async (blockHeight: number) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne();
|
||||
|
||||
return model.create({
|
||||
lastInterrepBlockScanned: blockHeight,
|
||||
});
|
||||
if (result) {
|
||||
return result.update({
|
||||
lastArbitrumBlockScanned: blockHeight,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const updateLastArbitrumBlock = async (blockHeight: number) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne();
|
||||
return model.create({
|
||||
lastArbitrumBlockScanned: blockHeight,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (result) {
|
||||
return result.update({
|
||||
lastArbitrumBlockScanned: blockHeight,
|
||||
});
|
||||
}
|
||||
const updateLastGroup42BlockScanned = async (blockHeight: number) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne();
|
||||
|
||||
return model.create({
|
||||
lastArbitrumBlockScanned: blockHeight,
|
||||
});
|
||||
if (result) {
|
||||
return result.update({
|
||||
lastGroup42BlockScanned: blockHeight,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const updateLastGroup42BlockScanned = async (blockHeight: number) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne();
|
||||
return model.create({
|
||||
lastGroup42BlockScanned: blockHeight,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (result) {
|
||||
return result.update({
|
||||
lastGroup42BlockScanned: blockHeight,
|
||||
});
|
||||
}
|
||||
|
||||
return model.create({
|
||||
lastGroup42BlockScanned: blockHeight,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
read,
|
||||
updateLastENSBlock,
|
||||
updateLastInterrepBlock,
|
||||
updateLastArbitrumBlock,
|
||||
updateLastGroup42BlockScanned,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
read,
|
||||
updateLastENSBlock,
|
||||
updateLastInterrepBlock,
|
||||
updateLastArbitrumBlock,
|
||||
updateLastGroup42BlockScanned,
|
||||
};
|
||||
};
|
||||
|
||||
export default app;
|
||||
|
||||
@@ -2,126 +2,126 @@ import { BIGINT, ModelCtor, Sequelize, STRING } from 'sequelize';
|
||||
import { ConnectionMessageSubType } from '../util/message';
|
||||
|
||||
type ConnectionModel = {
|
||||
messageId: string;
|
||||
hash: string;
|
||||
creator: string;
|
||||
type: string;
|
||||
subtype: string;
|
||||
createdAt: number;
|
||||
name: string;
|
||||
messageId: string;
|
||||
hash: string;
|
||||
creator: string;
|
||||
type: string;
|
||||
subtype: string;
|
||||
createdAt: number;
|
||||
name: string;
|
||||
};
|
||||
|
||||
const connections = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'connections',
|
||||
{
|
||||
messageId: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
hash: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
},
|
||||
creator: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
type: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
subtype: {
|
||||
type: STRING,
|
||||
},
|
||||
createdAt: {
|
||||
type: BIGINT,
|
||||
allowNull: false,
|
||||
},
|
||||
name: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['creator'] },
|
||||
{ fields: ['subtype'] },
|
||||
{ fields: ['name'] },
|
||||
{ fields: ['hash'], unique: true },
|
||||
{ fields: ['messageId'], unique: true },
|
||||
],
|
||||
}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'connections',
|
||||
{
|
||||
messageId: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
hash: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
},
|
||||
creator: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
type: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
subtype: {
|
||||
type: STRING,
|
||||
},
|
||||
createdAt: {
|
||||
type: BIGINT,
|
||||
allowNull: false,
|
||||
},
|
||||
name: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['creator'] },
|
||||
{ fields: ['subtype'] },
|
||||
{ fields: ['name'] },
|
||||
{ fields: ['hash'], unique: true },
|
||||
{ fields: ['messageId'], unique: true },
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
const findOne = async (hash: string): Promise<ConnectionModel | null> => {
|
||||
let result: any = await model.findOne({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
const findOne = async (hash: string): Promise<ConnectionModel | null> => {
|
||||
let result: any = await model.findOne({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) return null;
|
||||
if (!result) return null;
|
||||
|
||||
const json = result.toJSON() as ConnectionModel;
|
||||
const json = result.toJSON() as ConnectionModel;
|
||||
|
||||
return json;
|
||||
};
|
||||
return json;
|
||||
};
|
||||
|
||||
const remove = async (hash: string) => {
|
||||
return model.destroy({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
};
|
||||
const remove = async (hash: string) => {
|
||||
return model.destroy({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findAllByTargetName = async (
|
||||
name: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC'
|
||||
): Promise<ConnectionModel[]> => {
|
||||
let result = await model.findAll({
|
||||
where: {
|
||||
name,
|
||||
},
|
||||
offset,
|
||||
limit,
|
||||
order: [['createdAt', order]],
|
||||
});
|
||||
const findAllByTargetName = async (
|
||||
name: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC'
|
||||
): Promise<ConnectionModel[]> => {
|
||||
let result = await model.findAll({
|
||||
where: {
|
||||
name,
|
||||
},
|
||||
offset,
|
||||
limit,
|
||||
order: [['createdAt', order]],
|
||||
});
|
||||
|
||||
return result.map((r: any) => r.toJSON() as ConnectionModel);
|
||||
};
|
||||
return result.map((r: any) => r.toJSON() as ConnectionModel);
|
||||
};
|
||||
|
||||
const createConnection = async (record: ConnectionModel) => {
|
||||
return model.create(record);
|
||||
};
|
||||
const createConnection = async (record: ConnectionModel) => {
|
||||
return model.create(record);
|
||||
};
|
||||
|
||||
const findMemberInvite = async (groupAddress: string, memberAddress: string) => {
|
||||
let result: any = await model.findOne({
|
||||
where: {
|
||||
creator: groupAddress,
|
||||
name: memberAddress,
|
||||
subtype: ConnectionMessageSubType.MemberInvite,
|
||||
},
|
||||
});
|
||||
const findMemberInvite = async (groupAddress: string, memberAddress: string) => {
|
||||
let result: any = await model.findOne({
|
||||
where: {
|
||||
creator: groupAddress,
|
||||
name: memberAddress,
|
||||
subtype: ConnectionMessageSubType.MemberInvite,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) return null;
|
||||
if (!result) return null;
|
||||
|
||||
const json = result.toJSON() as ConnectionModel;
|
||||
const json = result.toJSON() as ConnectionModel;
|
||||
|
||||
return json;
|
||||
};
|
||||
return json;
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
findOne,
|
||||
remove,
|
||||
findAllByTargetName,
|
||||
createConnection,
|
||||
findMemberInvite,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
findOne,
|
||||
remove,
|
||||
findAllByTargetName,
|
||||
createConnection,
|
||||
findMemberInvite,
|
||||
};
|
||||
};
|
||||
|
||||
export default connections;
|
||||
|
||||
@@ -1,74 +1,74 @@
|
||||
import { Sequelize, BIGINT, STRING } from 'sequelize';
|
||||
|
||||
type ENSModel = {
|
||||
ens: string;
|
||||
address: string;
|
||||
ens: string;
|
||||
address: string;
|
||||
};
|
||||
|
||||
const ens = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'ens',
|
||||
{
|
||||
ens: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
validate: {
|
||||
notEmpty: true,
|
||||
},
|
||||
primaryKey: true,
|
||||
unique: true,
|
||||
},
|
||||
address: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
validate: {
|
||||
notEmpty: true,
|
||||
},
|
||||
},
|
||||
const model = sequelize.define(
|
||||
'ens',
|
||||
{
|
||||
ens: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
validate: {
|
||||
notEmpty: true,
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['ens'] }, { fields: ['address'] }],
|
||||
}
|
||||
);
|
||||
primaryKey: true,
|
||||
unique: true,
|
||||
},
|
||||
address: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
validate: {
|
||||
notEmpty: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['ens'] }, { fields: ['address'] }],
|
||||
}
|
||||
);
|
||||
|
||||
const readByAddress = async (address: string): Promise<ENSModel> => {
|
||||
let result = await model.findOne({
|
||||
where: { address },
|
||||
});
|
||||
return result?.toJSON() as ENSModel;
|
||||
};
|
||||
const readByAddress = async (address: string): Promise<ENSModel> => {
|
||||
let result = await model.findOne({
|
||||
where: { address },
|
||||
});
|
||||
return result?.toJSON() as ENSModel;
|
||||
};
|
||||
|
||||
const readByENS = async (ens: string): Promise<ENSModel> => {
|
||||
let result = await model.findOne({
|
||||
where: { ens },
|
||||
});
|
||||
return result?.toJSON() as ENSModel;
|
||||
};
|
||||
const readByENS = async (ens: string): Promise<ENSModel> => {
|
||||
let result = await model.findOne({
|
||||
where: { ens },
|
||||
});
|
||||
return result?.toJSON() as ENSModel;
|
||||
};
|
||||
|
||||
const update = async (ens: string, address: string) => {
|
||||
const result = await model.findOne({
|
||||
where: { ens },
|
||||
});
|
||||
const update = async (ens: string, address: string) => {
|
||||
const result = await model.findOne({
|
||||
where: { ens },
|
||||
});
|
||||
|
||||
if (result) {
|
||||
return result.update({
|
||||
ens,
|
||||
address,
|
||||
});
|
||||
}
|
||||
if (result) {
|
||||
return result.update({
|
||||
ens,
|
||||
address,
|
||||
});
|
||||
}
|
||||
|
||||
return model.create({
|
||||
ens,
|
||||
address,
|
||||
});
|
||||
};
|
||||
return model.create({
|
||||
ens,
|
||||
address,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
readByENS,
|
||||
readByAddress,
|
||||
update,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
readByENS,
|
||||
readByAddress,
|
||||
update,
|
||||
};
|
||||
};
|
||||
|
||||
export default ens;
|
||||
|
||||
@@ -2,92 +2,92 @@ import { Sequelize, STRING } from 'sequelize';
|
||||
import { Mutex } from 'async-mutex';
|
||||
|
||||
type InterepGroupModel = {
|
||||
provider: string;
|
||||
name: string;
|
||||
root_hash: string;
|
||||
provider: string;
|
||||
name: string;
|
||||
root_hash: string;
|
||||
};
|
||||
|
||||
const mutex = new Mutex();
|
||||
|
||||
const interepGroups = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'interep_groups',
|
||||
{
|
||||
name: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
provider: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
root_hash: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
const model = sequelize.define(
|
||||
'interep_groups',
|
||||
{
|
||||
name: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
provider: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
root_hash: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['root_hash'] },
|
||||
{ fields: ['provider'] },
|
||||
{ fields: ['name'] },
|
||||
{ fields: ['provider', 'name'], unique: true },
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
const findOneByHash = async (root_hash: string): Promise<InterepGroupModel | null> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
root_hash,
|
||||
},
|
||||
});
|
||||
|
||||
return result?.toJSON() as InterepGroupModel;
|
||||
};
|
||||
|
||||
const addHash = async (root_hash: string, provider: string, name: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
provider,
|
||||
name,
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['root_hash'] },
|
||||
{ fields: ['provider'] },
|
||||
{ fields: ['name'] },
|
||||
{ fields: ['provider', 'name'], unique: true },
|
||||
],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const findOneByHash = async (root_hash: string): Promise<InterepGroupModel | null> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
root_hash,
|
||||
},
|
||||
if (!result) {
|
||||
return model.create({
|
||||
root_hash,
|
||||
provider,
|
||||
name,
|
||||
});
|
||||
|
||||
return result?.toJSON() as InterepGroupModel;
|
||||
};
|
||||
|
||||
const addHash = async (root_hash: string, provider: string, name: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
provider,
|
||||
name,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return model.create({
|
||||
root_hash,
|
||||
provider,
|
||||
name,
|
||||
});
|
||||
} else {
|
||||
return result.update({
|
||||
root_hash,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return result.update({
|
||||
root_hash,
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getGroup = async (provider: string, name: string): Promise<InterepGroupModel> => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
provider,
|
||||
name,
|
||||
},
|
||||
});
|
||||
const getGroup = async (provider: string, name: string): Promise<InterepGroupModel> => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
provider,
|
||||
name,
|
||||
},
|
||||
});
|
||||
|
||||
return result?.toJSON() as InterepGroupModel;
|
||||
});
|
||||
};
|
||||
return result?.toJSON() as InterepGroupModel;
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
findOneByHash,
|
||||
addHash,
|
||||
getGroup,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
findOneByHash,
|
||||
addHash,
|
||||
getGroup,
|
||||
};
|
||||
};
|
||||
|
||||
export default interepGroups;
|
||||
|
||||
@@ -1,76 +1,76 @@
|
||||
import { Sequelize, BIGINT, STRING } from 'sequelize';
|
||||
|
||||
type LinkPreviewModel = {
|
||||
link: string;
|
||||
title: string;
|
||||
description: string;
|
||||
image: string;
|
||||
mediaType: string;
|
||||
contentType: string;
|
||||
favicon: string;
|
||||
link: string;
|
||||
title: string;
|
||||
description: string;
|
||||
image: string;
|
||||
mediaType: string;
|
||||
contentType: string;
|
||||
favicon: string;
|
||||
};
|
||||
|
||||
const linkPreview = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'link',
|
||||
{
|
||||
title: {
|
||||
type: STRING,
|
||||
},
|
||||
image: {
|
||||
type: STRING,
|
||||
},
|
||||
description: {
|
||||
type: STRING,
|
||||
},
|
||||
link: {
|
||||
type: STRING,
|
||||
unique: true,
|
||||
},
|
||||
mediaType: {
|
||||
type: STRING,
|
||||
},
|
||||
contentType: {
|
||||
type: STRING,
|
||||
},
|
||||
favicon: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['link'], unique: true }],
|
||||
}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'link',
|
||||
{
|
||||
title: {
|
||||
type: STRING,
|
||||
},
|
||||
image: {
|
||||
type: STRING,
|
||||
},
|
||||
description: {
|
||||
type: STRING,
|
||||
},
|
||||
link: {
|
||||
type: STRING,
|
||||
unique: true,
|
||||
},
|
||||
mediaType: {
|
||||
type: STRING,
|
||||
},
|
||||
contentType: {
|
||||
type: STRING,
|
||||
},
|
||||
favicon: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['link'], unique: true }],
|
||||
}
|
||||
);
|
||||
|
||||
const read = async (link: string): Promise<LinkPreviewModel> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
link,
|
||||
},
|
||||
});
|
||||
const read = async (link: string): Promise<LinkPreviewModel> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
link,
|
||||
},
|
||||
});
|
||||
|
||||
return result?.toJSON() as LinkPreviewModel;
|
||||
};
|
||||
return result?.toJSON() as LinkPreviewModel;
|
||||
};
|
||||
|
||||
const update = async (linkPreview: LinkPreviewModel) => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
link: linkPreview.link,
|
||||
},
|
||||
});
|
||||
const update = async (linkPreview: LinkPreviewModel) => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
link: linkPreview.link,
|
||||
},
|
||||
});
|
||||
|
||||
if (result) {
|
||||
return result.update(linkPreview);
|
||||
}
|
||||
if (result) {
|
||||
return result.update(linkPreview);
|
||||
}
|
||||
|
||||
return model.create(linkPreview);
|
||||
};
|
||||
return model.create(linkPreview);
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
read,
|
||||
update,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
read,
|
||||
update,
|
||||
};
|
||||
};
|
||||
|
||||
export default linkPreview;
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
import { Sequelize, BIGINT, STRING } from 'sequelize';
|
||||
|
||||
type MerkleRootModel = {
|
||||
root_hash: string;
|
||||
group_id: string;
|
||||
root_hash: string;
|
||||
group_id: string;
|
||||
};
|
||||
|
||||
const merkleRoot = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'merkle_root',
|
||||
{
|
||||
root_hash: {
|
||||
type: STRING,
|
||||
},
|
||||
group_id: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['root_hash'], unique: true }, { fields: ['group_id'] }],
|
||||
}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'merkle_root',
|
||||
{
|
||||
root_hash: {
|
||||
type: STRING,
|
||||
},
|
||||
group_id: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['root_hash'], unique: true }, { fields: ['group_id'] }],
|
||||
}
|
||||
);
|
||||
|
||||
const getGroupByRoot = async (root_hash: string): Promise<MerkleRootModel> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
root_hash,
|
||||
},
|
||||
});
|
||||
const getGroupByRoot = async (root_hash: string): Promise<MerkleRootModel> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
root_hash,
|
||||
},
|
||||
});
|
||||
|
||||
return result?.toJSON() as MerkleRootModel;
|
||||
};
|
||||
return result?.toJSON() as MerkleRootModel;
|
||||
};
|
||||
|
||||
const addRoot = async (root_hash: string, group_id: string) => {
|
||||
const exist = await model.findOne({
|
||||
where: {
|
||||
root_hash,
|
||||
},
|
||||
});
|
||||
const addRoot = async (root_hash: string, group_id: string) => {
|
||||
const exist = await model.findOne({
|
||||
where: {
|
||||
root_hash,
|
||||
},
|
||||
});
|
||||
|
||||
if (!exist) {
|
||||
return model.create({
|
||||
root_hash,
|
||||
group_id,
|
||||
});
|
||||
}
|
||||
};
|
||||
if (!exist) {
|
||||
return model.create({
|
||||
root_hash,
|
||||
group_id,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
addRoot,
|
||||
getGroupByRoot,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
addRoot,
|
||||
getGroupByRoot,
|
||||
};
|
||||
};
|
||||
|
||||
export default merkleRoot;
|
||||
|
||||
@@ -2,90 +2,90 @@ import { BIGINT, QueryTypes, Sequelize, STRING } from 'sequelize';
|
||||
import { Mutex } from 'async-mutex';
|
||||
|
||||
type MetaModel = {
|
||||
reference: string;
|
||||
replyCount: number;
|
||||
likeCount: number;
|
||||
repostCount: number;
|
||||
postCount: number;
|
||||
reference: string;
|
||||
replyCount: number;
|
||||
likeCount: number;
|
||||
repostCount: number;
|
||||
postCount: number;
|
||||
};
|
||||
|
||||
const mutex = new Mutex();
|
||||
|
||||
const emptyMeta = {
|
||||
replyCount: 0,
|
||||
likeCount: 0,
|
||||
repostCount: 0,
|
||||
postCount: 0,
|
||||
replyCount: 0,
|
||||
likeCount: 0,
|
||||
repostCount: 0,
|
||||
postCount: 0,
|
||||
};
|
||||
|
||||
const meta = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'meta',
|
||||
{
|
||||
reference: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
},
|
||||
postCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
replyCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
likeCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
repostCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['reference'], unique: true }],
|
||||
}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'meta',
|
||||
{
|
||||
reference: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
},
|
||||
postCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
replyCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
likeCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
repostCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['reference'], unique: true }],
|
||||
}
|
||||
);
|
||||
|
||||
const findOne = async (reference: string, context = ''): Promise<any | null> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
const findOne = async (reference: string, context = ''): Promise<any | null> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
${selectMetaQuery}
|
||||
`,
|
||||
{
|
||||
replacements: {
|
||||
context: context || '',
|
||||
reference,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
{
|
||||
replacements: {
|
||||
context: context || '',
|
||||
reference,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
|
||||
const values: any[] = [];
|
||||
const values: any[] = [];
|
||||
|
||||
for (let r of result) {
|
||||
const row = r as any;
|
||||
const meta = {
|
||||
liked: row.liked,
|
||||
reposted: row.reposted,
|
||||
replyCount: row.replyCount ? Number(row.replyCount) : 0,
|
||||
repostCount: row.repostCount ? Number(row.repostCount) : 0,
|
||||
likeCount: row.likeCount ? Number(row.likeCount) : 0,
|
||||
postCount: row.postCount ? Number(row.postCount) : 0,
|
||||
};
|
||||
values.push(meta);
|
||||
}
|
||||
for (let r of result) {
|
||||
const row = r as any;
|
||||
const meta = {
|
||||
liked: row.liked,
|
||||
reposted: row.reposted,
|
||||
replyCount: row.replyCount ? Number(row.replyCount) : 0,
|
||||
repostCount: row.repostCount ? Number(row.repostCount) : 0,
|
||||
likeCount: row.likeCount ? Number(row.likeCount) : 0,
|
||||
postCount: row.postCount ? Number(row.postCount) : 0,
|
||||
};
|
||||
values.push(meta);
|
||||
}
|
||||
|
||||
return values[0]
|
||||
? values[0]
|
||||
: {
|
||||
liked: null,
|
||||
reposted: null,
|
||||
...emptyMeta,
|
||||
};
|
||||
};
|
||||
return values[0]
|
||||
? values[0]
|
||||
: {
|
||||
liked: null,
|
||||
reposted: null,
|
||||
...emptyMeta,
|
||||
};
|
||||
};
|
||||
|
||||
const findTags = async (offset = 0, limit = 20) => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
const findTags = async (offset = 0, limit = 20) => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
SELECT
|
||||
"reference",
|
||||
"postCount"
|
||||
@@ -94,68 +94,68 @@ const meta = (sequelize: Sequelize) => {
|
||||
ORDER BY "postCount" DESC
|
||||
LIMIT :limit OFFSET :offset
|
||||
`,
|
||||
{
|
||||
replacements: {
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
{
|
||||
replacements: {
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
|
||||
return result.map((r: any) => ({
|
||||
tagName: r.reference,
|
||||
postCount: Number(r.postCount),
|
||||
}));
|
||||
return result.map((r: any) => ({
|
||||
tagName: r.reference,
|
||||
postCount: Number(r.postCount),
|
||||
}));
|
||||
};
|
||||
|
||||
const update = async (record: MetaModel) => {
|
||||
return model.create(record);
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
findOne,
|
||||
findTags,
|
||||
update,
|
||||
addLike: makeIncrementer('likeCount', 1),
|
||||
addReply: makeIncrementer('replyCount', 1),
|
||||
addRepost: makeIncrementer('repostCount', 1),
|
||||
addPost: makeIncrementer('postCount', 1),
|
||||
removeLike: makeIncrementer('likeCount', -1),
|
||||
removeReply: makeIncrementer('replyCount', -1),
|
||||
removeRepost: makeIncrementer('repostCount', -1),
|
||||
removePost: makeIncrementer('postCount', -1),
|
||||
};
|
||||
|
||||
function makeIncrementer(key: string, delta: number) {
|
||||
return async (reference: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
reference,
|
||||
},
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const data = result.toJSON() as MetaModel;
|
||||
return result.update({
|
||||
...data,
|
||||
// @ts-ignore
|
||||
[key]: Math.max(0, (Number(data[key]) || 0) + delta),
|
||||
});
|
||||
}
|
||||
|
||||
const res = await model.create({
|
||||
reference,
|
||||
...emptyMeta,
|
||||
[key]: Math.max(0, delta),
|
||||
});
|
||||
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
||||
const update = async (record: MetaModel) => {
|
||||
return model.create(record);
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
findOne,
|
||||
findTags,
|
||||
update,
|
||||
addLike: makeIncrementer('likeCount', 1),
|
||||
addReply: makeIncrementer('replyCount', 1),
|
||||
addRepost: makeIncrementer('repostCount', 1),
|
||||
addPost: makeIncrementer('postCount', 1),
|
||||
removeLike: makeIncrementer('likeCount', -1),
|
||||
removeReply: makeIncrementer('replyCount', -1),
|
||||
removeRepost: makeIncrementer('repostCount', -1),
|
||||
removePost: makeIncrementer('postCount', -1),
|
||||
};
|
||||
|
||||
function makeIncrementer(key: string, delta: number) {
|
||||
return async (reference: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
reference,
|
||||
},
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const data = result.toJSON() as MetaModel;
|
||||
return result.update({
|
||||
...data,
|
||||
// @ts-ignore
|
||||
[key]: Math.max(0, (Number(data[key]) || 0) + delta),
|
||||
});
|
||||
}
|
||||
|
||||
const res = await model.create({
|
||||
reference,
|
||||
...emptyMeta,
|
||||
[key]: Math.max(0, delta),
|
||||
});
|
||||
|
||||
return res;
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
@@ -1,90 +1,90 @@
|
||||
import { BIGINT, ModelCtor, QueryTypes, Sequelize, STRING } from 'sequelize';
|
||||
import {
|
||||
Message,
|
||||
MessageType,
|
||||
ModerationJSON,
|
||||
PostJSON,
|
||||
PostMessageSubType,
|
||||
Message,
|
||||
MessageType,
|
||||
ModerationJSON,
|
||||
PostJSON,
|
||||
PostMessageSubType,
|
||||
} from '../util/message';
|
||||
import { PostModel } from './posts';
|
||||
|
||||
type ModerationModel = {
|
||||
messageId: string;
|
||||
hash: string;
|
||||
creator: string;
|
||||
type: string;
|
||||
subtype: string;
|
||||
createdAt: number;
|
||||
reference: string;
|
||||
messageId: string;
|
||||
hash: string;
|
||||
creator: string;
|
||||
type: string;
|
||||
subtype: string;
|
||||
createdAt: number;
|
||||
reference: string;
|
||||
};
|
||||
|
||||
const moderations = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'moderations',
|
||||
{
|
||||
messageId: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
hash: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
},
|
||||
creator: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
type: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
subtype: {
|
||||
type: STRING,
|
||||
},
|
||||
createdAt: {
|
||||
type: BIGINT,
|
||||
allowNull: false,
|
||||
},
|
||||
reference: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['creator'] },
|
||||
{ fields: ['subtype'] },
|
||||
{ fields: ['hash'], unique: true },
|
||||
{ fields: ['messageId'], unique: true },
|
||||
],
|
||||
}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'moderations',
|
||||
{
|
||||
messageId: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
hash: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
},
|
||||
creator: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
type: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
subtype: {
|
||||
type: STRING,
|
||||
},
|
||||
createdAt: {
|
||||
type: BIGINT,
|
||||
allowNull: false,
|
||||
},
|
||||
reference: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['creator'] },
|
||||
{ fields: ['subtype'] },
|
||||
{ fields: ['hash'], unique: true },
|
||||
{ fields: ['messageId'], unique: true },
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
const findOne = async (hash: string): Promise<ModerationModel | null> => {
|
||||
let result: any = await model.findOne({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
const findOne = async (hash: string): Promise<ModerationModel | null> => {
|
||||
let result: any = await model.findOne({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) return null;
|
||||
if (!result) return null;
|
||||
|
||||
const json = result.toJSON() as ModerationModel;
|
||||
const json = result.toJSON() as ModerationModel;
|
||||
|
||||
return json;
|
||||
};
|
||||
return json;
|
||||
};
|
||||
|
||||
const remove = async (hash: string) => {
|
||||
return model.destroy({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
};
|
||||
const remove = async (hash: string) => {
|
||||
return model.destroy({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findThreadModeration = async (message_id: string) => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
const findThreadModeration = async (message_id: string) => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
SELECT
|
||||
m."messageId",
|
||||
m.creator,
|
||||
@@ -99,51 +99,51 @@ const moderations = (sequelize: Sequelize) => {
|
||||
JOIN moderations m ON m.reference = t.message_id AND m.creator = p.creator AND m.subtype IN ('THREAD_HIDE_BLOCK', 'THREAD_ONLY_MENTION', 'THREAD_SHOW_FOLLOW')
|
||||
WHERE t.message_id = :message_id
|
||||
`,
|
||||
{
|
||||
replacements: {
|
||||
message_id,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
{
|
||||
replacements: {
|
||||
message_id,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
return null;
|
||||
};
|
||||
|
||||
const findAllByReference = async (
|
||||
reference: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC'
|
||||
): Promise<ModerationJSON[]> => {
|
||||
let result = await model.findAll({
|
||||
where: {
|
||||
reference,
|
||||
},
|
||||
offset,
|
||||
limit,
|
||||
order: [['createdAt', order]],
|
||||
});
|
||||
const findAllByReference = async (
|
||||
reference: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC'
|
||||
): Promise<ModerationJSON[]> => {
|
||||
let result = await model.findAll({
|
||||
where: {
|
||||
reference,
|
||||
},
|
||||
offset,
|
||||
limit,
|
||||
order: [['createdAt', order]],
|
||||
});
|
||||
|
||||
return result.map((r: any) => r.toJSON() as ModerationJSON);
|
||||
};
|
||||
return result.map((r: any) => r.toJSON() as ModerationJSON);
|
||||
};
|
||||
|
||||
const createModeration = async (record: ModerationModel) => {
|
||||
return model.create(record);
|
||||
};
|
||||
const createModeration = async (record: ModerationModel) => {
|
||||
return model.create(record);
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
findOne,
|
||||
remove,
|
||||
findAllByReference,
|
||||
findThreadModeration,
|
||||
createModeration,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
findOne,
|
||||
remove,
|
||||
findAllByReference,
|
||||
findThreadModeration,
|
||||
createModeration,
|
||||
};
|
||||
};
|
||||
|
||||
export default moderations;
|
||||
|
||||
@@ -3,137 +3,137 @@ import { MessageType, PostJSON, PostMessageSubType } from '../util/message';
|
||||
import { Mutex } from 'async-mutex';
|
||||
import bodyParser from 'body-parser';
|
||||
import {
|
||||
globalModClause,
|
||||
globalVisibilityClause,
|
||||
notBlockedClause,
|
||||
replyModerationClause,
|
||||
globalModClause,
|
||||
globalVisibilityClause,
|
||||
notBlockedClause,
|
||||
replyModerationClause,
|
||||
} from '../util/sql';
|
||||
import config from '../util/config';
|
||||
|
||||
const mutex = new Mutex();
|
||||
|
||||
export type PostModel = {
|
||||
messageId: string;
|
||||
hash: string;
|
||||
proof?: string;
|
||||
signals?: string;
|
||||
creator: string;
|
||||
type: string;
|
||||
subtype: string;
|
||||
createdAt: number;
|
||||
topic: string;
|
||||
title: string;
|
||||
content: string;
|
||||
reference: string;
|
||||
attachment: string;
|
||||
messageId: string;
|
||||
hash: string;
|
||||
proof?: string;
|
||||
signals?: string;
|
||||
creator: string;
|
||||
type: string;
|
||||
subtype: string;
|
||||
createdAt: number;
|
||||
topic: string;
|
||||
title: string;
|
||||
content: string;
|
||||
reference: string;
|
||||
attachment: string;
|
||||
};
|
||||
|
||||
const posts = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'posts',
|
||||
{
|
||||
hash: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
},
|
||||
messageId: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
creator: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
proof: {
|
||||
type: STRING(65535),
|
||||
},
|
||||
signals: {
|
||||
type: STRING(65535),
|
||||
},
|
||||
type: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
subtype: {
|
||||
type: STRING,
|
||||
},
|
||||
createdAt: {
|
||||
type: BIGINT,
|
||||
allowNull: false,
|
||||
},
|
||||
topic: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
title: {
|
||||
type: STRING(4095),
|
||||
allowNull: false,
|
||||
},
|
||||
content: {
|
||||
type: STRING(65535),
|
||||
allowNull: false,
|
||||
},
|
||||
reference: {
|
||||
type: STRING,
|
||||
},
|
||||
attachment: {
|
||||
type: STRING(4095),
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['creator'] },
|
||||
{ fields: ['subtype'] },
|
||||
{ fields: ['topic'] },
|
||||
{ fields: ['reference'] },
|
||||
{ fields: ['hash'], unique: true },
|
||||
{ fields: ['messageId'], unique: true },
|
||||
],
|
||||
}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'posts',
|
||||
{
|
||||
hash: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
},
|
||||
messageId: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
creator: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
proof: {
|
||||
type: STRING(65535),
|
||||
},
|
||||
signals: {
|
||||
type: STRING(65535),
|
||||
},
|
||||
type: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
subtype: {
|
||||
type: STRING,
|
||||
},
|
||||
createdAt: {
|
||||
type: BIGINT,
|
||||
allowNull: false,
|
||||
},
|
||||
topic: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
title: {
|
||||
type: STRING(4095),
|
||||
allowNull: false,
|
||||
},
|
||||
content: {
|
||||
type: STRING(65535),
|
||||
allowNull: false,
|
||||
},
|
||||
reference: {
|
||||
type: STRING,
|
||||
},
|
||||
attachment: {
|
||||
type: STRING(4095),
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['creator'] },
|
||||
{ fields: ['subtype'] },
|
||||
{ fields: ['topic'] },
|
||||
{ fields: ['reference'] },
|
||||
{ fields: ['hash'], unique: true },
|
||||
{ fields: ['messageId'], unique: true },
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
const remove = async (hash: string) => {
|
||||
return model.destroy({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
};
|
||||
const remove = async (hash: string) => {
|
||||
return model.destroy({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findRoot = async (messageId: string): Promise<string | null> => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
messageId,
|
||||
},
|
||||
});
|
||||
const findRoot = async (messageId: string): Promise<string | null> => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
messageId,
|
||||
},
|
||||
});
|
||||
|
||||
if (result) {
|
||||
// @ts-ignore
|
||||
const json: PostModel = result.toJSON();
|
||||
if (json.reference && json.subtype === 'REPLY') {
|
||||
return findRoot(json.reference);
|
||||
}
|
||||
if (result) {
|
||||
// @ts-ignore
|
||||
const json: PostModel = result.toJSON();
|
||||
if (json.reference && json.subtype === 'REPLY') {
|
||||
return findRoot(json.reference);
|
||||
}
|
||||
|
||||
if (json.reference && json.subtype === 'M_REPLY') {
|
||||
return findRoot(json.reference);
|
||||
}
|
||||
if (json.reference && json.subtype === 'M_REPLY') {
|
||||
return findRoot(json.reference);
|
||||
}
|
||||
|
||||
if (json.reference && json.subtype === 'REPOST') {
|
||||
return findRoot(json.reference);
|
||||
}
|
||||
if (json.reference && json.subtype === 'REPOST') {
|
||||
return findRoot(json.reference);
|
||||
}
|
||||
|
||||
if (!json.reference) {
|
||||
return json.messageId;
|
||||
}
|
||||
}
|
||||
if (!json.reference) {
|
||||
return json.messageId;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
return null;
|
||||
};
|
||||
|
||||
const findOne = async (hash: string, context?: string): Promise<PostJSON | null> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
const findOne = async (hash: string, context?: string): Promise<PostJSON | null> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
${selectJoinQuery}
|
||||
WHERE (
|
||||
p.hash = :hash
|
||||
@@ -141,37 +141,37 @@ const posts = (sequelize: Sequelize) => {
|
||||
AND p."creator" NOT IN (SELECT name FROM connections WHERE name = p.creator AND creator = :context AND subtype = 'BLOCK')
|
||||
)
|
||||
`,
|
||||
{
|
||||
replacements: {
|
||||
context: context || '',
|
||||
hash,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
{
|
||||
replacements: {
|
||||
context: context || '',
|
||||
hash,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
|
||||
const values: PostJSON[] = [];
|
||||
const values: PostJSON[] = [];
|
||||
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
if (post.createdAt > 0) {
|
||||
values.push(post);
|
||||
}
|
||||
}
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
if (post.createdAt > 0) {
|
||||
values.push(post);
|
||||
}
|
||||
}
|
||||
|
||||
return values[0];
|
||||
};
|
||||
return values[0];
|
||||
};
|
||||
|
||||
const findAllPosts = async (
|
||||
creator?: string,
|
||||
context?: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC',
|
||||
showAll = false
|
||||
): Promise<PostJSON[]> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
const findAllPosts = async (
|
||||
creator?: string,
|
||||
context?: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC',
|
||||
showAll = false
|
||||
): Promise<PostJSON[]> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
${selectJoinQuery}
|
||||
WHERE (
|
||||
(p.type = 'POST' AND p.subtype IN ('', 'M_POST', 'REPOST'))
|
||||
@@ -184,36 +184,36 @@ const posts = (sequelize: Sequelize) => {
|
||||
ORDER BY p."createdAt" ${order}
|
||||
LIMIT :limit OFFSET :offset
|
||||
`,
|
||||
{
|
||||
replacements: {
|
||||
context: context || '',
|
||||
creator: creator || '',
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
{
|
||||
replacements: {
|
||||
context: context || '',
|
||||
creator: creator || '',
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
|
||||
const values: PostJSON[] = [];
|
||||
const values: PostJSON[] = [];
|
||||
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
values.push(post);
|
||||
}
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
values.push(post);
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
return values;
|
||||
};
|
||||
|
||||
const findAllRepliesFromCreator = async (
|
||||
creator?: string,
|
||||
context?: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC'
|
||||
): Promise<PostJSON[]> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
const findAllRepliesFromCreator = async (
|
||||
creator?: string,
|
||||
context?: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC'
|
||||
): Promise<PostJSON[]> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
${selectJoinQuery}
|
||||
WHERE (
|
||||
p.type = 'POST'
|
||||
@@ -226,35 +226,35 @@ const posts = (sequelize: Sequelize) => {
|
||||
ORDER BY p."createdAt" ${order}
|
||||
LIMIT :limit OFFSET :offset
|
||||
`,
|
||||
{
|
||||
replacements: {
|
||||
context: context || '',
|
||||
creator: creator || '',
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
{
|
||||
replacements: {
|
||||
context: context || '',
|
||||
creator: creator || '',
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
|
||||
const values: PostJSON[] = [];
|
||||
const values: PostJSON[] = [];
|
||||
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
values.push(post);
|
||||
}
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
values.push(post);
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
return values;
|
||||
};
|
||||
|
||||
const getHomeFeed = async (
|
||||
context?: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC'
|
||||
): Promise<PostJSON[]> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
const getHomeFeed = async (
|
||||
context?: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC'
|
||||
): Promise<PostJSON[]> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
${selectJoinQuery}
|
||||
WHERE (
|
||||
p.subtype != 'REPLY'
|
||||
@@ -269,37 +269,37 @@ const posts = (sequelize: Sequelize) => {
|
||||
ORDER BY p."createdAt" ${order}
|
||||
LIMIT :limit OFFSET :offset
|
||||
`,
|
||||
{
|
||||
replacements: {
|
||||
context: context || '',
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
{
|
||||
replacements: {
|
||||
context: context || '',
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
|
||||
const values: PostJSON[] = [];
|
||||
const values: PostJSON[] = [];
|
||||
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
values.push(post);
|
||||
}
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
values.push(post);
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
return values;
|
||||
};
|
||||
|
||||
const findAllReplies = async (
|
||||
reference: string,
|
||||
context?: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'ASC',
|
||||
tweetId = '',
|
||||
unmoderated = false
|
||||
): Promise<PostJSON[]> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
const findAllReplies = async (
|
||||
reference: string,
|
||||
context?: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'ASC',
|
||||
tweetId = '',
|
||||
unmoderated = false
|
||||
): Promise<PostJSON[]> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
${selectJoinQuery}
|
||||
WHERE (
|
||||
(
|
||||
@@ -313,35 +313,35 @@ const posts = (sequelize: Sequelize) => {
|
||||
ORDER BY p."createdAt" ${order}
|
||||
LIMIT :limit OFFSET :offset
|
||||
`,
|
||||
{
|
||||
replacements: {
|
||||
reference,
|
||||
context: context || '',
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
{
|
||||
replacements: {
|
||||
reference,
|
||||
context: context || '',
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
|
||||
const values: PostJSON[] = [];
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
values.push(post);
|
||||
}
|
||||
const values: PostJSON[] = [];
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
values.push(post);
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
return values;
|
||||
};
|
||||
|
||||
const findAllLikedPostsByCreator = async (
|
||||
creator: string,
|
||||
context?: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC'
|
||||
): Promise<PostJSON[]> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
const findAllLikedPostsByCreator = async (
|
||||
creator: string,
|
||||
context?: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC'
|
||||
): Promise<PostJSON[]> => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
${selectLikedPostsQuery}
|
||||
WHERE (
|
||||
p."createdAt" != -1
|
||||
@@ -354,196 +354,193 @@ const posts = (sequelize: Sequelize) => {
|
||||
ORDER BY p."createdAt" ${order}
|
||||
LIMIT :limit OFFSET :offset
|
||||
`,
|
||||
{
|
||||
replacements: {
|
||||
creator,
|
||||
context: context || '',
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
{
|
||||
replacements: {
|
||||
creator,
|
||||
context: context || '',
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
|
||||
const values: PostJSON[] = [];
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
values.push(post);
|
||||
const values: PostJSON[] = [];
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
values.push(post);
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
const findLastTweetInConversation = async (id: string) => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{
|
||||
reference: id,
|
||||
type: '@TWEET@',
|
||||
},
|
||||
],
|
||||
},
|
||||
order: [['createdAt', 'DESC']],
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
return result?.toJSON();
|
||||
};
|
||||
|
||||
const createTwitterPosts = async (records: PostModel[]) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
for (let record of records) {
|
||||
if (record.type !== '@TWEET@') continue;
|
||||
|
||||
const topic = `https://twitter.com/${record.creator}/status/${record.messageId}`;
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{
|
||||
topic: topic,
|
||||
subtype: [PostMessageSubType.MirrorPost, PostMessageSubType.MirrorReply],
|
||||
},
|
||||
{
|
||||
messageId: record.messageId,
|
||||
type: '@TWEET@',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
if (result) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
await model.create(record);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const findLastTweetInConversation = async (id: string) => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{
|
||||
reference: id,
|
||||
type: '@TWEET@',
|
||||
},
|
||||
],
|
||||
},
|
||||
order: [['createdAt', 'DESC']],
|
||||
limit: 1,
|
||||
});
|
||||
const createPost = async (record: PostModel) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
hash: record.hash,
|
||||
},
|
||||
});
|
||||
|
||||
return result?.toJSON();
|
||||
};
|
||||
if (result) {
|
||||
const json = (await result.toJSON()) as PostModel;
|
||||
if (json.createdAt < 0) {
|
||||
// @ts-ignore
|
||||
await result.changed('createdAt', true);
|
||||
await result.set('createdAt', record.createdAt, { raw: true });
|
||||
await result.save({
|
||||
fields: ['createdAt'],
|
||||
});
|
||||
return result.update(record);
|
||||
}
|
||||
}
|
||||
|
||||
const createTwitterPosts = async (records: PostModel[]) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
for (let record of records) {
|
||||
if (record.type !== '@TWEET@') continue;
|
||||
return model.create(record);
|
||||
});
|
||||
};
|
||||
|
||||
const topic = `https://twitter.com/${record.creator}/status/${record.messageId}`;
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
[Op.or]: [
|
||||
{
|
||||
topic: topic,
|
||||
subtype: [
|
||||
PostMessageSubType.MirrorPost,
|
||||
PostMessageSubType.MirrorReply,
|
||||
],
|
||||
},
|
||||
{
|
||||
messageId: record.messageId,
|
||||
type: '@TWEET@',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const ensurePost = async (messageId: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const [creator, hash] = messageId.split('/');
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
hash: hash || creator,
|
||||
},
|
||||
});
|
||||
|
||||
if (result) {
|
||||
continue;
|
||||
}
|
||||
if (!result) {
|
||||
const emptyModel: PostModel = {
|
||||
messageId: messageId,
|
||||
hash: hash || creator,
|
||||
type: MessageType.Post,
|
||||
subtype: PostMessageSubType.Default,
|
||||
creator: hash ? creator : '',
|
||||
createdAt: -1,
|
||||
topic: '',
|
||||
title: '',
|
||||
content: '',
|
||||
reference: '',
|
||||
attachment: '',
|
||||
};
|
||||
return model.create(emptyModel);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
await model.create(record);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const createPost = async (record: PostModel) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
hash: record.hash,
|
||||
},
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const json = (await result.toJSON()) as PostModel;
|
||||
if (json.createdAt < 0) {
|
||||
// @ts-ignore
|
||||
await result.changed('createdAt', true);
|
||||
await result.set('createdAt', record.createdAt, { raw: true });
|
||||
await result.save({
|
||||
fields: ['createdAt'],
|
||||
});
|
||||
return result.update(record);
|
||||
}
|
||||
}
|
||||
|
||||
return model.create(record);
|
||||
});
|
||||
};
|
||||
|
||||
const ensurePost = async (messageId: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const [creator, hash] = messageId.split('/');
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
hash: hash || creator,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
const emptyModel: PostModel = {
|
||||
messageId: messageId,
|
||||
hash: hash || creator,
|
||||
type: MessageType.Post,
|
||||
subtype: PostMessageSubType.Default,
|
||||
creator: hash ? creator : '',
|
||||
createdAt: -1,
|
||||
topic: '',
|
||||
title: '',
|
||||
content: '',
|
||||
reference: '',
|
||||
attachment: '',
|
||||
};
|
||||
return model.create(emptyModel);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
remove,
|
||||
findOne,
|
||||
findRoot,
|
||||
findAllPosts,
|
||||
findAllRepliesFromCreator,
|
||||
findAllLikedPostsByCreator,
|
||||
findLastTweetInConversation,
|
||||
findAllReplies,
|
||||
getHomeFeed,
|
||||
createTwitterPosts,
|
||||
createPost,
|
||||
ensurePost,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
remove,
|
||||
findOne,
|
||||
findRoot,
|
||||
findAllPosts,
|
||||
findAllRepliesFromCreator,
|
||||
findAllLikedPostsByCreator,
|
||||
findLastTweetInConversation,
|
||||
findAllReplies,
|
||||
getHomeFeed,
|
||||
createTwitterPosts,
|
||||
createPost,
|
||||
ensurePost,
|
||||
};
|
||||
};
|
||||
|
||||
export default posts;
|
||||
|
||||
export function inflateResultToPostJSON(r: any): PostJSON {
|
||||
const json = r as any;
|
||||
const meta = {
|
||||
replyCount: +json?.replyCount || 0,
|
||||
likeCount: +json?.likeCount || 0,
|
||||
repostCount: +json?.repostCount || 0,
|
||||
liked: json?.liked,
|
||||
reposted: json?.reposted,
|
||||
blocked: json?.blocked,
|
||||
interepProvider: json?.interepProvider,
|
||||
interepGroup: json?.interepGroup,
|
||||
rootId: json?.rootId,
|
||||
moderation: json?.moderation || null,
|
||||
modblockedctx: json?.modblockedctx || null,
|
||||
modfollowedctx: json?.modfollowedctx || null,
|
||||
modmentionedctx: json?.modmentionedctx || null,
|
||||
modLikedPost: json?.modLikedPost || null,
|
||||
modBlockedPost: json?.modBlockedPost || null,
|
||||
modBlockedUser: json?.modBlockedUser || null,
|
||||
modFollowerUser: json?.modFollowerUser || null,
|
||||
};
|
||||
const json = r as any;
|
||||
const meta = {
|
||||
replyCount: +json?.replyCount || 0,
|
||||
likeCount: +json?.likeCount || 0,
|
||||
repostCount: +json?.repostCount || 0,
|
||||
liked: json?.liked,
|
||||
reposted: json?.reposted,
|
||||
blocked: json?.blocked,
|
||||
interepProvider: json?.interepProvider,
|
||||
interepGroup: json?.interepGroup,
|
||||
rootId: json?.rootId,
|
||||
moderation: json?.moderation || null,
|
||||
modblockedctx: json?.modblockedctx || null,
|
||||
modfollowedctx: json?.modfollowedctx || null,
|
||||
modmentionedctx: json?.modmentionedctx || null,
|
||||
modLikedPost: json?.modLikedPost || null,
|
||||
modBlockedPost: json?.modBlockedPost || null,
|
||||
modBlockedUser: json?.modBlockedUser || null,
|
||||
modFollowerUser: json?.modFollowerUser || null,
|
||||
};
|
||||
|
||||
if (json.subtype === PostMessageSubType.Repost) {
|
||||
meta.replyCount = +json?.rpReplyCount || 0;
|
||||
meta.likeCount = +json?.rpLikeCount || 0;
|
||||
meta.repostCount = +json?.rpRepostCount || 0;
|
||||
meta.liked = json?.rpLiked || null;
|
||||
meta.blocked = json?.rpBLocked || null;
|
||||
meta.reposted = json?.rpReposted || null;
|
||||
meta.interepProvider = json?.rpInterepProvider || null;
|
||||
meta.interepGroup = json?.rpInterepGroup || null;
|
||||
}
|
||||
if (json.subtype === PostMessageSubType.Repost) {
|
||||
meta.replyCount = +json?.rpReplyCount || 0;
|
||||
meta.likeCount = +json?.rpLikeCount || 0;
|
||||
meta.repostCount = +json?.rpRepostCount || 0;
|
||||
meta.liked = json?.rpLiked || null;
|
||||
meta.blocked = json?.rpBLocked || null;
|
||||
meta.reposted = json?.rpReposted || null;
|
||||
meta.interepProvider = json?.rpInterepProvider || null;
|
||||
meta.interepGroup = json?.rpInterepGroup || null;
|
||||
}
|
||||
|
||||
return {
|
||||
type: json.type as MessageType,
|
||||
subtype: json.subtype as PostMessageSubType,
|
||||
messageId: json.creator ? `${json.creator}/${json.hash}` : json.hash,
|
||||
hash: json.hash,
|
||||
createdAt: json.createdAt,
|
||||
payload: {
|
||||
topic: json.topic,
|
||||
title: json.title,
|
||||
content: json.content,
|
||||
reference: json.reference,
|
||||
attachment: json.attachment,
|
||||
},
|
||||
meta: meta,
|
||||
};
|
||||
return {
|
||||
type: json.type as MessageType,
|
||||
subtype: json.subtype as PostMessageSubType,
|
||||
messageId: json.creator ? `${json.creator}/${json.hash}` : json.hash,
|
||||
hash: json.hash,
|
||||
createdAt: json.createdAt,
|
||||
payload: {
|
||||
topic: json.topic,
|
||||
title: json.title,
|
||||
content: json.content,
|
||||
reference: json.reference,
|
||||
attachment: json.attachment,
|
||||
},
|
||||
meta: meta,
|
||||
};
|
||||
}
|
||||
|
||||
const selectJoinQuery = `
|
||||
@@ -598,11 +595,11 @@ const selectJoinQuery = `
|
||||
LEFT JOIN connections modblockeduser ON modblockeduser."messageId" = (SELECT "messageId" FROM connections WHERE subtype = 'BLOCK' AND name = p."creator" AND creator = root.creator LIMIT 1)
|
||||
LEFT JOIN connections modfolloweduser ON modfolloweduser."messageId" = (SELECT "messageId" FROM connections WHERE subtype = 'FOLLOW' AND name = p."creator" AND creator = root.creator LIMIT 1)
|
||||
LEFT JOIN moderations gmodblocked ON gmodblocked."messageId" = (SELECT "messageId" FROM moderations WHERE subtype = 'BLOCK' AND reference = p."messageId" AND creator IN (${config.moderators
|
||||
.map(d => `'${d}'`)
|
||||
.join(',')}) LIMIT 1)
|
||||
.map(d => `'${d}'`)
|
||||
.join(',')}) LIMIT 1)
|
||||
LEFT JOIN connections gmodblockeduser ON gmodblockeduser."messageId" = (SELECT "messageId" FROM connections WHERE subtype = 'BLOCK' AND name = p."creator" AND creator IN (${config.moderators
|
||||
.map(d => `'${d}'`)
|
||||
.join(',')}) LIMIT 1)
|
||||
.map(d => `'${d}'`)
|
||||
.join(',')}) LIMIT 1)
|
||||
LEFT JOIN posts rp ON rp."messageId" = (SELECT "messageId" from posts WHERE p."messageId" = reference AND creator = :context AND subtype = 'REPOST' LIMIT 1)
|
||||
LEFT JOIN posts rprp ON rprp."messageId" = (SELECT "messageId" from posts WHERE reference = p.reference AND creator = :context AND subtype = 'REPOST' AND p.subtype = 'REPOST' LIMIT 1)
|
||||
LEFT JOIN meta mt ON mt."reference" = p."messageId"
|
||||
@@ -666,11 +663,11 @@ const selectLikedPostsQuery = `
|
||||
LEFT JOIN moderations modblocked ON modblocked."messageId" = (SELECT "messageId" FROM moderations WHERE subtype = 'BLOCK' AND reference = p."messageId" AND creator = root.creator LIMIT 1)
|
||||
LEFT JOIN connections modblockeduser ON modblockeduser."messageId" = (SELECT "messageId" FROM connections WHERE subtype = 'BLOCK' AND name = p."creator" AND creator = root.creator LIMIT 1)
|
||||
LEFT JOIN moderations gmodblocked ON gmodblocked."messageId" = (SELECT "messageId" FROM moderations WHERE subtype = 'BLOCK' AND reference = p."messageId" AND creator IN (${config.moderators
|
||||
.map(d => `'${d}'`)
|
||||
.join(',')}) LIMIT 1)
|
||||
.map(d => `'${d}'`)
|
||||
.join(',')}) LIMIT 1)
|
||||
LEFT JOIN connections gmodblockeduser ON gmodblockeduser."messageId" = (SELECT "messageId" FROM connections WHERE subtype = 'BLOCK' AND name = p."creator" AND creator IN (${config.moderators
|
||||
.map(d => `'${d}'`)
|
||||
.join(',')}) LIMIT 1)
|
||||
.map(d => `'${d}'`)
|
||||
.join(',')}) LIMIT 1)
|
||||
LEFT JOIN connections modfolloweduser ON modfolloweduser."messageId" = (SELECT "messageId" FROM connections WHERE subtype = 'FOLLOW' AND name = p."creator" AND creator = root.creator LIMIT 1)
|
||||
LEFT JOIN posts rp ON rp."messageId" = (SELECT "messageId" from posts WHERE p."messageId" = reference AND creator = :context AND subtype = 'REPOST' LIMIT 1)
|
||||
LEFT JOIN posts rprp ON rprp."messageId" = (SELECT "messageId" from posts WHERE reference = p.reference AND creator = :context AND subtype = 'REPOST' AND p.subtype = 'REPOST' LIMIT 1)
|
||||
|
||||
@@ -2,103 +2,103 @@ import { BIGINT, Sequelize, STRING } from 'sequelize';
|
||||
import { ProfileMessageSubType } from '../util/message';
|
||||
|
||||
type ProfileModel = {
|
||||
messageId: string;
|
||||
hash: string;
|
||||
creator: string;
|
||||
type: string;
|
||||
subtype: string;
|
||||
createdAt: number;
|
||||
key: string;
|
||||
value: string;
|
||||
messageId: string;
|
||||
hash: string;
|
||||
creator: string;
|
||||
type: string;
|
||||
subtype: string;
|
||||
createdAt: number;
|
||||
key: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type UserProfile = {
|
||||
name: string;
|
||||
bio: string;
|
||||
coverImage: string;
|
||||
profileImage: string;
|
||||
twitterVerification: string;
|
||||
website: string;
|
||||
name: string;
|
||||
bio: string;
|
||||
coverImage: string;
|
||||
profileImage: string;
|
||||
twitterVerification: string;
|
||||
website: string;
|
||||
};
|
||||
|
||||
const profiles = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'profiles',
|
||||
{
|
||||
messageId: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
hash: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
},
|
||||
creator: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
type: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
subtype: {
|
||||
type: STRING,
|
||||
},
|
||||
createdAt: {
|
||||
type: BIGINT,
|
||||
allowNull: false,
|
||||
},
|
||||
key: {
|
||||
type: STRING,
|
||||
},
|
||||
value: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['creator'] },
|
||||
{ fields: ['subtype'] },
|
||||
{ fields: ['key'] },
|
||||
{ fields: ['hash'], unique: true },
|
||||
{ fields: ['messageId'], unique: true },
|
||||
],
|
||||
}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'profiles',
|
||||
{
|
||||
messageId: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
hash: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
},
|
||||
creator: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
type: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
subtype: {
|
||||
type: STRING,
|
||||
},
|
||||
createdAt: {
|
||||
type: BIGINT,
|
||||
allowNull: false,
|
||||
},
|
||||
key: {
|
||||
type: STRING,
|
||||
},
|
||||
value: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['creator'] },
|
||||
{ fields: ['subtype'] },
|
||||
{ fields: ['key'] },
|
||||
{ fields: ['hash'], unique: true },
|
||||
{ fields: ['messageId'], unique: true },
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
const remove = async (hash: string) => {
|
||||
return model.destroy({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
};
|
||||
const remove = async (hash: string) => {
|
||||
return model.destroy({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findOne = async (hash: string): Promise<ProfileModel | null> => {
|
||||
let result: any = await model.findOne({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
const findOne = async (hash: string): Promise<ProfileModel | null> => {
|
||||
let result: any = await model.findOne({
|
||||
where: {
|
||||
hash,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) return null;
|
||||
if (!result) return null;
|
||||
|
||||
const json = result.toJSON() as ProfileModel;
|
||||
const json = result.toJSON() as ProfileModel;
|
||||
|
||||
return json;
|
||||
};
|
||||
return json;
|
||||
};
|
||||
|
||||
const createProfile = async (record: ProfileModel) => {
|
||||
return model.create(record);
|
||||
};
|
||||
const createProfile = async (record: ProfileModel) => {
|
||||
return model.create(record);
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
remove,
|
||||
findOne,
|
||||
createProfile,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
remove,
|
||||
findOne,
|
||||
createProfile,
|
||||
};
|
||||
};
|
||||
|
||||
export default profiles;
|
||||
|
||||
@@ -1,104 +1,104 @@
|
||||
import { BIGINT, Sequelize, STRING } from 'sequelize';
|
||||
|
||||
type RecordModel = {
|
||||
soul: string;
|
||||
field: string;
|
||||
value: string;
|
||||
relation: string;
|
||||
state: number;
|
||||
soul: string;
|
||||
field: string;
|
||||
value: string;
|
||||
relation: string;
|
||||
state: number;
|
||||
};
|
||||
|
||||
const records = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'records',
|
||||
const model = sequelize.define(
|
||||
'records',
|
||||
{
|
||||
soul: {
|
||||
type: STRING(4095),
|
||||
},
|
||||
field: {
|
||||
type: STRING(4095),
|
||||
},
|
||||
value: {
|
||||
type: STRING(65535),
|
||||
},
|
||||
relation: {
|
||||
type: STRING(65535),
|
||||
},
|
||||
state: {
|
||||
type: BIGINT,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{
|
||||
soul: {
|
||||
type: STRING(4095),
|
||||
},
|
||||
field: {
|
||||
type: STRING(4095),
|
||||
},
|
||||
value: {
|
||||
type: STRING(65535),
|
||||
},
|
||||
relation: {
|
||||
type: STRING(65535),
|
||||
},
|
||||
state: {
|
||||
type: BIGINT,
|
||||
},
|
||||
fields: ['soul'],
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{
|
||||
fields: ['soul'],
|
||||
},
|
||||
{
|
||||
unique: true,
|
||||
fields: ['soul', 'field'],
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
unique: true,
|
||||
fields: ['soul', 'field'],
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
const findOne = async (soul: string, field: string): Promise<RecordModel | null> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
soul,
|
||||
field,
|
||||
},
|
||||
});
|
||||
const findOne = async (soul: string, field: string): Promise<RecordModel | null> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
soul,
|
||||
field,
|
||||
},
|
||||
});
|
||||
|
||||
return (result?.toJSON() as RecordModel) || null;
|
||||
};
|
||||
return (result?.toJSON() as RecordModel) || null;
|
||||
};
|
||||
|
||||
const findAll = async (soul: string): Promise<RecordModel[]> => {
|
||||
let result = await model.findAll({
|
||||
where: {
|
||||
soul,
|
||||
},
|
||||
});
|
||||
const findAll = async (soul: string): Promise<RecordModel[]> => {
|
||||
let result = await model.findAll({
|
||||
where: {
|
||||
soul,
|
||||
},
|
||||
});
|
||||
|
||||
return result.map(r => r.toJSON() as RecordModel);
|
||||
};
|
||||
return result.map(r => r.toJSON() as RecordModel);
|
||||
};
|
||||
|
||||
const readAll = async (offset = 0, limit = 20): Promise<RecordModel[]> => {
|
||||
let result = await model.findAll({
|
||||
offset,
|
||||
limit,
|
||||
});
|
||||
const readAll = async (offset = 0, limit = 20): Promise<RecordModel[]> => {
|
||||
let result = await model.findAll({
|
||||
offset,
|
||||
limit,
|
||||
});
|
||||
|
||||
return result.map(r => r.toJSON() as RecordModel);
|
||||
};
|
||||
return result.map(r => r.toJSON() as RecordModel);
|
||||
};
|
||||
|
||||
const updateOrCreateRecord = async (record: RecordModel) => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
soul: record.soul,
|
||||
field: record.field,
|
||||
},
|
||||
});
|
||||
const updateOrCreateRecord = async (record: RecordModel) => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
soul: record.soul,
|
||||
field: record.field,
|
||||
},
|
||||
});
|
||||
|
||||
const json = result?.toJSON() as RecordModel;
|
||||
const json = result?.toJSON() as RecordModel;
|
||||
|
||||
if (json?.state >= record.state) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (json?.state >= record.state) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (result) {
|
||||
return result.update(record);
|
||||
}
|
||||
if (result) {
|
||||
return result.update(record);
|
||||
}
|
||||
|
||||
return model.create(record);
|
||||
};
|
||||
return model.create(record);
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
findOne,
|
||||
findAll,
|
||||
readAll,
|
||||
updateOrCreateRecord,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
findOne,
|
||||
findAll,
|
||||
readAll,
|
||||
updateOrCreateRecord,
|
||||
};
|
||||
};
|
||||
|
||||
export default records;
|
||||
|
||||
@@ -2,127 +2,123 @@ import { Sequelize, STRING } from 'sequelize';
|
||||
import { Mutex } from 'async-mutex';
|
||||
|
||||
type SemaphoreModel = {
|
||||
id_commitment: string;
|
||||
group_id: string;
|
||||
root_hash: string;
|
||||
id_commitment: string;
|
||||
group_id: string;
|
||||
root_hash: string;
|
||||
};
|
||||
|
||||
const mutex = new Mutex();
|
||||
|
||||
const semaphore = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'semaphore',
|
||||
{
|
||||
id_commitment: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
group_id: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
root_hash: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
const model = sequelize.define(
|
||||
'semaphore',
|
||||
{
|
||||
id_commitment: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
group_id: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
root_hash: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['id_commitment'] }, { fields: ['group_id'] }, { fields: ['root_hash'] }],
|
||||
}
|
||||
);
|
||||
|
||||
const findOneByCommitment = async (id_commitment: string): Promise<SemaphoreModel | null> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
id_commitment,
|
||||
},
|
||||
order: [['createdAt', 'DESC']],
|
||||
});
|
||||
|
||||
return result?.toJSON() as SemaphoreModel;
|
||||
};
|
||||
|
||||
const findOne = async (
|
||||
id_commitment: string,
|
||||
group_id: string
|
||||
): Promise<SemaphoreModel | null> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
id_commitment,
|
||||
group_id,
|
||||
},
|
||||
order: [['createdAt', 'DESC']],
|
||||
});
|
||||
|
||||
return result?.toJSON() as SemaphoreModel;
|
||||
};
|
||||
|
||||
const findAllByCommitment = async (id_commitment: string): Promise<SemaphoreModel[]> => {
|
||||
let result = await model.findAll({
|
||||
where: {
|
||||
id_commitment,
|
||||
},
|
||||
});
|
||||
|
||||
return result.map(r => r.toJSON()) as SemaphoreModel[];
|
||||
};
|
||||
|
||||
const findAllByGroup = async (group: string): Promise<SemaphoreModel[]> => {
|
||||
let result = await model.findAll({
|
||||
where: {
|
||||
group,
|
||||
},
|
||||
});
|
||||
|
||||
return result.map(r => r.toJSON()) as SemaphoreModel[];
|
||||
};
|
||||
|
||||
const addID = async (id_commitment: string, group_id: string, root_hash: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
id_commitment,
|
||||
group_id,
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['id_commitment'] },
|
||||
{ fields: ['group_id'] },
|
||||
{ fields: ['root_hash'] },
|
||||
],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const findOneByCommitment = async (id_commitment: string): Promise<SemaphoreModel | null> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
id_commitment,
|
||||
},
|
||||
order: [['createdAt', 'DESC']],
|
||||
if (!result) {
|
||||
return model.create({
|
||||
id_commitment,
|
||||
group_id,
|
||||
root_hash,
|
||||
});
|
||||
} else {
|
||||
return result.update({ root_hash });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return result?.toJSON() as SemaphoreModel;
|
||||
};
|
||||
const removeID = async (id_commitment: string, group_id: string, root_hash: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
return model.destroy({
|
||||
where: {
|
||||
id_commitment,
|
||||
group_id,
|
||||
root_hash,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const findOne = async (
|
||||
id_commitment: string,
|
||||
group_id: string
|
||||
): Promise<SemaphoreModel | null> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
id_commitment,
|
||||
group_id,
|
||||
},
|
||||
order: [['createdAt', 'DESC']],
|
||||
});
|
||||
|
||||
return result?.toJSON() as SemaphoreModel;
|
||||
};
|
||||
|
||||
const findAllByCommitment = async (id_commitment: string): Promise<SemaphoreModel[]> => {
|
||||
let result = await model.findAll({
|
||||
where: {
|
||||
id_commitment,
|
||||
},
|
||||
});
|
||||
|
||||
return result.map(r => r.toJSON()) as SemaphoreModel[];
|
||||
};
|
||||
|
||||
const findAllByGroup = async (group: string): Promise<SemaphoreModel[]> => {
|
||||
let result = await model.findAll({
|
||||
where: {
|
||||
group,
|
||||
},
|
||||
});
|
||||
|
||||
return result.map(r => r.toJSON()) as SemaphoreModel[];
|
||||
};
|
||||
|
||||
const addID = async (id_commitment: string, group_id: string, root_hash: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
id_commitment,
|
||||
group_id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return model.create({
|
||||
id_commitment,
|
||||
group_id,
|
||||
root_hash,
|
||||
});
|
||||
} else {
|
||||
return result.update({ root_hash });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const removeID = async (id_commitment: string, group_id: string, root_hash: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
return model.destroy({
|
||||
where: {
|
||||
id_commitment,
|
||||
group_id,
|
||||
root_hash,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
findOne,
|
||||
findOneByCommitment,
|
||||
findAllByCommitment,
|
||||
addID,
|
||||
removeID,
|
||||
findAllByGroup,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
findOne,
|
||||
findOneByCommitment,
|
||||
findAllByCommitment,
|
||||
addID,
|
||||
removeID,
|
||||
findAllByGroup,
|
||||
};
|
||||
};
|
||||
|
||||
export default semaphore;
|
||||
|
||||
@@ -2,52 +2,52 @@ import { Sequelize, BIGINT, STRING, QueryTypes } from 'sequelize';
|
||||
import { Mutex } from 'async-mutex';
|
||||
|
||||
type SemaphoreCreatorModel = {
|
||||
message_id: string;
|
||||
provider: string;
|
||||
group: string;
|
||||
message_id: string;
|
||||
provider: string;
|
||||
group: string;
|
||||
};
|
||||
|
||||
const mutex = new Mutex();
|
||||
|
||||
const semaphoreCreators = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'semaphore_creators',
|
||||
{
|
||||
group: {
|
||||
type: STRING,
|
||||
},
|
||||
provider: {
|
||||
type: STRING,
|
||||
},
|
||||
message_id: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['provider'] },
|
||||
{ fields: ['group'] },
|
||||
{ fields: ['message_id'], unique: true },
|
||||
],
|
||||
}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'semaphore_creators',
|
||||
{
|
||||
group: {
|
||||
type: STRING,
|
||||
},
|
||||
provider: {
|
||||
type: STRING,
|
||||
},
|
||||
message_id: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['provider'] },
|
||||
{ fields: ['group'] },
|
||||
{ fields: ['message_id'], unique: true },
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
const addSemaphoreCreator = async (messageId: string, provider: string, group: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const res = await model.create({
|
||||
provider: provider,
|
||||
group: group,
|
||||
message_id: messageId,
|
||||
});
|
||||
const addSemaphoreCreator = async (messageId: string, provider: string, group: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const res = await model.create({
|
||||
provider: provider,
|
||||
group: group,
|
||||
message_id: messageId,
|
||||
});
|
||||
|
||||
return res;
|
||||
});
|
||||
};
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
addSemaphoreCreator,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
addSemaphoreCreator,
|
||||
};
|
||||
};
|
||||
|
||||
export default semaphoreCreators;
|
||||
|
||||
@@ -6,65 +6,65 @@ import { globalModClause, globalVisibilityClause, replyModerationClause } from '
|
||||
import config from '../util/config';
|
||||
|
||||
type TagModel = {
|
||||
tag_name: string;
|
||||
message_id: string;
|
||||
tag_name: string;
|
||||
message_id: string;
|
||||
};
|
||||
|
||||
const mutex = new Mutex();
|
||||
|
||||
const tags = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'tags',
|
||||
{
|
||||
tag_name: {
|
||||
type: STRING,
|
||||
},
|
||||
message_id: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['tag_name', 'message_id'], unique: true }],
|
||||
}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'tags',
|
||||
{
|
||||
tag_name: {
|
||||
type: STRING,
|
||||
},
|
||||
message_id: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['tag_name', 'message_id'], unique: true }],
|
||||
}
|
||||
);
|
||||
|
||||
const addTagPost = async (tagName: string, messageId: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const res = await model.create({
|
||||
tag_name: tagName,
|
||||
message_id: messageId,
|
||||
});
|
||||
const addTagPost = async (tagName: string, messageId: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const res = await model.create({
|
||||
tag_name: tagName,
|
||||
message_id: messageId,
|
||||
});
|
||||
|
||||
return res;
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
||||
const removeTagPost = async (tagName: string, messageId: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
try {
|
||||
const res = await model.destroy({
|
||||
where: {
|
||||
tag_name: tagName,
|
||||
message_id: messageId,
|
||||
},
|
||||
});
|
||||
};
|
||||
return res;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const removeTagPost = async (tagName: string, messageId: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
try {
|
||||
const res = await model.destroy({
|
||||
where: {
|
||||
tag_name: tagName,
|
||||
message_id: messageId,
|
||||
},
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getPostsByTag = async (
|
||||
tagName: string,
|
||||
context?: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC',
|
||||
showAll = false
|
||||
) => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
const getPostsByTag = async (
|
||||
tagName: string,
|
||||
context?: string,
|
||||
offset = 0,
|
||||
limit = 20,
|
||||
order: 'DESC' | 'ASC' = 'DESC',
|
||||
showAll = false
|
||||
) => {
|
||||
const result = await sequelize.query(
|
||||
`
|
||||
${selectTagPostsQuery}
|
||||
WHERE (
|
||||
(p."createdAt" != -1)
|
||||
@@ -80,33 +80,33 @@ const tags = (sequelize: Sequelize) => {
|
||||
ORDER BY p."createdAt" ${order}
|
||||
LIMIT :limit OFFSET :offset
|
||||
`,
|
||||
{
|
||||
replacements: {
|
||||
context: context || '',
|
||||
limit,
|
||||
offset,
|
||||
tagName,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
{
|
||||
replacements: {
|
||||
context: context || '',
|
||||
limit,
|
||||
offset,
|
||||
tagName,
|
||||
},
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
);
|
||||
|
||||
const values: PostJSON[] = [];
|
||||
const values: PostJSON[] = [];
|
||||
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
values.push(post);
|
||||
}
|
||||
for (let r of result) {
|
||||
const post = inflateResultToPostJSON(r);
|
||||
values.push(post);
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
return values;
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
getPostsByTag,
|
||||
addTagPost,
|
||||
removeTagPost,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
getPostsByTag,
|
||||
addTagPost,
|
||||
removeTagPost,
|
||||
};
|
||||
};
|
||||
|
||||
export default tags;
|
||||
@@ -162,11 +162,11 @@ const selectTagPostsQuery = `
|
||||
LEFT JOIN moderations modliked ON modliked."messageId" = (SELECT "messageId" FROM moderations WHERE subtype = 'LIKE' AND reference = p."messageId" AND creator = root.creator LIMIT 1)
|
||||
LEFT JOIN moderations modblocked ON modblocked."messageId" = (SELECT "messageId" FROM moderations WHERE subtype = 'BLOCK' AND reference = p."messageId" AND creator = root.creator LIMIT 1)
|
||||
LEFT JOIN moderations gmodblocked ON gmodblocked."messageId" = (SELECT "messageId" FROM moderations WHERE subtype = 'BLOCK' AND reference = p."messageId" AND creator IN (${config.moderators
|
||||
.map(d => `'${d}'`)
|
||||
.join(',')}) LIMIT 1)
|
||||
.map(d => `'${d}'`)
|
||||
.join(',')}) LIMIT 1)
|
||||
LEFT JOIN connections gmodblockeduser ON gmodblockeduser."messageId" = (SELECT "messageId" FROM connections WHERE subtype = 'BLOCK' AND name = p."creator" AND creator IN (${config.moderators
|
||||
.map(d => `'${d}'`)
|
||||
.join(',')}) LIMIT 1)
|
||||
.map(d => `'${d}'`)
|
||||
.join(',')}) LIMIT 1)
|
||||
LEFT JOIN connections modblockeduser ON modblockeduser."messageId" = (SELECT "messageId" FROM connections WHERE subtype = 'BLOCK' AND name = p."creator" AND creator = root.creator LIMIT 1)
|
||||
LEFT JOIN connections modfolloweduser ON modfolloweduser."messageId" = (SELECT "messageId" FROM connections WHERE subtype = 'FOLLOW' AND name = p."creator" AND creator = root.creator LIMIT 1)
|
||||
LEFT JOIN posts rp ON rp."messageId" = (SELECT "messageId" from posts WHERE p."messageId" = reference AND creator = :context AND subtype = 'REPOST' LIMIT 1)
|
||||
|
||||
@@ -2,60 +2,60 @@ import { Sequelize, BIGINT, STRING, QueryTypes } from 'sequelize';
|
||||
import { Mutex } from 'async-mutex';
|
||||
|
||||
type ThreadModel = {
|
||||
message_id: string;
|
||||
root_id: string;
|
||||
message_id: string;
|
||||
root_id: string;
|
||||
};
|
||||
|
||||
const mutex = new Mutex();
|
||||
|
||||
const threads = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'threads',
|
||||
{
|
||||
message_id: {
|
||||
type: STRING,
|
||||
},
|
||||
root_id: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['root_id', 'message_id'], unique: true }],
|
||||
}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'threads',
|
||||
{
|
||||
message_id: {
|
||||
type: STRING,
|
||||
},
|
||||
root_id: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['root_id', 'message_id'], unique: true }],
|
||||
}
|
||||
);
|
||||
|
||||
const addThreadData = async (rootId: string, messageId: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const res = await model.create({
|
||||
root_id: rootId,
|
||||
message_id: messageId,
|
||||
});
|
||||
const addThreadData = async (rootId: string, messageId: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const res = await model.create({
|
||||
root_id: rootId,
|
||||
message_id: messageId,
|
||||
});
|
||||
|
||||
return res;
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
||||
const removeThreadData = async (rootId: string, messageId: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
try {
|
||||
const res = await model.destroy({
|
||||
where: {
|
||||
root_id: rootId,
|
||||
message_id: messageId,
|
||||
},
|
||||
});
|
||||
};
|
||||
return res;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const removeThreadData = async (rootId: string, messageId: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
try {
|
||||
const res = await model.destroy({
|
||||
where: {
|
||||
root_id: rootId,
|
||||
message_id: messageId,
|
||||
},
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
addThreadData,
|
||||
removeThreadData,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
addThreadData,
|
||||
removeThreadData,
|
||||
};
|
||||
};
|
||||
|
||||
export default threads;
|
||||
|
||||
@@ -3,149 +3,149 @@ import { Mutex } from 'async-mutex';
|
||||
const mutex = new Mutex();
|
||||
|
||||
type TwitterAuthModel = {
|
||||
userToken: string;
|
||||
userTokenSecret: string;
|
||||
userName: string;
|
||||
userId: string;
|
||||
userToken: string;
|
||||
userTokenSecret: string;
|
||||
userName: string;
|
||||
userId: string;
|
||||
};
|
||||
|
||||
const twitterAuth = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'twitter_auth',
|
||||
const model = sequelize.define(
|
||||
'twitter_auth',
|
||||
{
|
||||
user_token: {
|
||||
type: STRING,
|
||||
},
|
||||
user_token_secret: {
|
||||
type: STRING,
|
||||
},
|
||||
username: {
|
||||
type: STRING,
|
||||
},
|
||||
user_id: {
|
||||
type: STRING,
|
||||
},
|
||||
account: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{
|
||||
user_token: {
|
||||
type: STRING,
|
||||
},
|
||||
user_token_secret: {
|
||||
type: STRING,
|
||||
},
|
||||
username: {
|
||||
type: STRING,
|
||||
},
|
||||
user_id: {
|
||||
type: STRING,
|
||||
},
|
||||
account: {
|
||||
type: STRING,
|
||||
},
|
||||
unique: true,
|
||||
fields: ['user_token'],
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['user_token'],
|
||||
},
|
||||
{
|
||||
unique: true,
|
||||
fields: ['username'],
|
||||
},
|
||||
{
|
||||
unique: true,
|
||||
fields: ['user_id'],
|
||||
},
|
||||
{
|
||||
unique: true,
|
||||
fields: ['account'],
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
unique: true,
|
||||
fields: ['username'],
|
||||
},
|
||||
{
|
||||
unique: true,
|
||||
fields: ['user_id'],
|
||||
},
|
||||
{
|
||||
unique: true,
|
||||
fields: ['account'],
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
const findUserByToken = async (token?: string | null): Promise<TwitterAuthModel | null> => {
|
||||
if (!token) return null;
|
||||
const findUserByToken = async (token?: string | null): Promise<TwitterAuthModel | null> => {
|
||||
if (!token) return null;
|
||||
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
user_token: token,
|
||||
},
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
user_token: token,
|
||||
},
|
||||
});
|
||||
|
||||
return result?.toJSON() as TwitterAuthModel;
|
||||
};
|
||||
|
||||
const findUserByUsername = async (username: string): Promise<TwitterAuthModel | null> => {
|
||||
if (!username) return null;
|
||||
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
username: username,
|
||||
},
|
||||
});
|
||||
|
||||
return result?.toJSON() as TwitterAuthModel;
|
||||
};
|
||||
|
||||
const findUserByAccount = async (account: string): Promise<TwitterAuthModel | null> => {
|
||||
if (!account) return null;
|
||||
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
account: account,
|
||||
},
|
||||
});
|
||||
|
||||
return result?.toJSON() as TwitterAuthModel;
|
||||
};
|
||||
|
||||
const addAccount = async (username: string, account: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
username: username,
|
||||
},
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const json: any = await result?.toJSON();
|
||||
|
||||
if (json.username !== username) throw new Error(`${username} already exists`);
|
||||
|
||||
return result.update({
|
||||
account,
|
||||
});
|
||||
}
|
||||
|
||||
return result?.toJSON() as TwitterAuthModel;
|
||||
};
|
||||
return model.create({
|
||||
account,
|
||||
username,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const findUserByUsername = async (username: string): Promise<TwitterAuthModel | null> => {
|
||||
if (!username) return null;
|
||||
const updateUserToken = async (data: TwitterAuthModel) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
username: data.userName,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
username: username,
|
||||
},
|
||||
if (result) {
|
||||
return result.update({
|
||||
user_token: data.userToken,
|
||||
user_token_secret: data.userTokenSecret,
|
||||
username: data.userName,
|
||||
user_id: data.userId,
|
||||
});
|
||||
}
|
||||
|
||||
return result?.toJSON() as TwitterAuthModel;
|
||||
};
|
||||
return model.create({
|
||||
user_token: data.userToken,
|
||||
user_token_secret: data.userTokenSecret,
|
||||
username: data.userName,
|
||||
user_id: data.userId,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const findUserByAccount = async (account: string): Promise<TwitterAuthModel | null> => {
|
||||
if (!account) return null;
|
||||
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
account: account,
|
||||
},
|
||||
});
|
||||
|
||||
return result?.toJSON() as TwitterAuthModel;
|
||||
};
|
||||
|
||||
const addAccount = async (username: string, account: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
username: username,
|
||||
},
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const json: any = await result?.toJSON();
|
||||
|
||||
if (json.username !== username) throw new Error(`${username} already exists`);
|
||||
|
||||
return result.update({
|
||||
account,
|
||||
});
|
||||
}
|
||||
|
||||
return model.create({
|
||||
account,
|
||||
username,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const updateUserToken = async (data: TwitterAuthModel) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
username: data.userName,
|
||||
},
|
||||
});
|
||||
|
||||
if (result) {
|
||||
return result.update({
|
||||
user_token: data.userToken,
|
||||
user_token_secret: data.userTokenSecret,
|
||||
username: data.userName,
|
||||
user_id: data.userId,
|
||||
});
|
||||
}
|
||||
|
||||
return model.create({
|
||||
user_token: data.userToken,
|
||||
user_token_secret: data.userTokenSecret,
|
||||
username: data.userName,
|
||||
user_id: data.userId,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
addAccount,
|
||||
findUserByToken,
|
||||
findUserByAccount,
|
||||
findUserByUsername,
|
||||
updateUserToken,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
addAccount,
|
||||
findUserByToken,
|
||||
findUserByAccount,
|
||||
findUserByUsername,
|
||||
updateUserToken,
|
||||
};
|
||||
};
|
||||
|
||||
export default twitterAuth;
|
||||
|
||||
@@ -2,86 +2,86 @@ import { Mutex } from 'async-mutex';
|
||||
import { BIGINT, ENUM, QueryTypes, Sequelize, STRING } from 'sequelize';
|
||||
|
||||
export type UploadModel = {
|
||||
cid: string;
|
||||
filename: string;
|
||||
username: string;
|
||||
size: number;
|
||||
mimetype: string;
|
||||
cid: string;
|
||||
filename: string;
|
||||
username: string;
|
||||
size: number;
|
||||
mimetype: string;
|
||||
};
|
||||
|
||||
const mutex = new Mutex();
|
||||
|
||||
const uploads = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'uploads',
|
||||
{
|
||||
cid: {
|
||||
type: STRING,
|
||||
},
|
||||
filename: {
|
||||
type: STRING,
|
||||
},
|
||||
username: {
|
||||
type: STRING,
|
||||
},
|
||||
size: {
|
||||
type: BIGINT,
|
||||
},
|
||||
mimetype: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['cid'] },
|
||||
{ fields: ['filename'] },
|
||||
{ fields: ['username'] },
|
||||
{ fields: ['mimetype'] },
|
||||
],
|
||||
}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'uploads',
|
||||
{
|
||||
cid: {
|
||||
type: STRING,
|
||||
},
|
||||
filename: {
|
||||
type: STRING,
|
||||
},
|
||||
username: {
|
||||
type: STRING,
|
||||
},
|
||||
size: {
|
||||
type: BIGINT,
|
||||
},
|
||||
mimetype: {
|
||||
type: STRING,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['cid'] },
|
||||
{ fields: ['filename'] },
|
||||
{ fields: ['username'] },
|
||||
{ fields: ['mimetype'] },
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
const addUploadData = async (data: UploadModel) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const res = await model.create(data);
|
||||
return res;
|
||||
const addUploadData = async (data: UploadModel) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const res = await model.create(data);
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
||||
const removeUploadData = async (cid: string, filename: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
try {
|
||||
const res = await model.destroy({
|
||||
where: {
|
||||
cid,
|
||||
filename,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const removeUploadData = async (cid: string, filename: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
try {
|
||||
const res = await model.destroy({
|
||||
where: {
|
||||
cid,
|
||||
filename,
|
||||
},
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getTotalUploadByUser = async (username: string) => {
|
||||
const res = await model.sum('size', {
|
||||
where: {
|
||||
username,
|
||||
},
|
||||
});
|
||||
|
||||
if (isNaN(res)) return 0;
|
||||
|
||||
return res;
|
||||
};
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
addUploadData,
|
||||
removeUploadData,
|
||||
getTotalUploadByUser,
|
||||
};
|
||||
const getTotalUploadByUser = async (username: string) => {
|
||||
const res = await model.sum('size', {
|
||||
where: {
|
||||
username,
|
||||
},
|
||||
});
|
||||
|
||||
if (isNaN(res)) return 0;
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
addUploadData,
|
||||
removeUploadData,
|
||||
getTotalUploadByUser,
|
||||
};
|
||||
};
|
||||
|
||||
export default uploads;
|
||||
|
||||
@@ -2,121 +2,121 @@ import { BIGINT, Sequelize, STRING } from 'sequelize';
|
||||
import { Mutex } from 'async-mutex';
|
||||
|
||||
type UserMetaModel = {
|
||||
name: string;
|
||||
followerCount: number;
|
||||
followingCount: number;
|
||||
blockedCount: number;
|
||||
blockingCount: number;
|
||||
mentionedCount: number;
|
||||
postingCount: number;
|
||||
name: string;
|
||||
followerCount: number;
|
||||
followingCount: number;
|
||||
blockedCount: number;
|
||||
blockingCount: number;
|
||||
mentionedCount: number;
|
||||
postingCount: number;
|
||||
};
|
||||
|
||||
const mutex = new Mutex();
|
||||
|
||||
const emptyMeta = {
|
||||
followerCount: 0,
|
||||
followingCount: 0,
|
||||
blockedCount: 0,
|
||||
blockingCount: 0,
|
||||
mentionedCount: 0,
|
||||
postingCount: 0,
|
||||
followerCount: 0,
|
||||
followingCount: 0,
|
||||
blockedCount: 0,
|
||||
blockingCount: 0,
|
||||
mentionedCount: 0,
|
||||
postingCount: 0,
|
||||
};
|
||||
|
||||
const userMeta = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'usermeta',
|
||||
{
|
||||
name: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
},
|
||||
followerCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
followingCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
blockedCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
blockingCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
mentionedCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
postingCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['name'], unique: true }],
|
||||
}
|
||||
);
|
||||
const model = sequelize.define(
|
||||
'usermeta',
|
||||
{
|
||||
name: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
},
|
||||
followerCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
followingCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
blockedCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
blockingCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
mentionedCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
postingCount: {
|
||||
type: BIGINT,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [{ fields: ['name'], unique: true }],
|
||||
}
|
||||
);
|
||||
|
||||
const findOne = async (name: string): Promise<UserMetaModel | null> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
name,
|
||||
},
|
||||
const findOne = async (name: string): Promise<UserMetaModel | null> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
name,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
(result?.toJSON() as UserMetaModel) || {
|
||||
...emptyMeta,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const update = async (record: UserMetaModel) => {
|
||||
return model.create(record);
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
update,
|
||||
findOne,
|
||||
addFollower: makeKeyIncrementer('followerCount', 1),
|
||||
addFollowing: makeKeyIncrementer('followingCount', 1),
|
||||
addBlocked: makeKeyIncrementer('blockedCount', 1),
|
||||
addBlocking: makeKeyIncrementer('blockingCount', 1),
|
||||
addPostingCount: makeKeyIncrementer('postingCount', 1),
|
||||
addMentionedCount: makeKeyIncrementer('mentionedCount', 1),
|
||||
removeFollower: makeKeyIncrementer('followerCount', -1),
|
||||
removeFollowing: makeKeyIncrementer('followingCount', -1),
|
||||
removeBlocked: makeKeyIncrementer('blockedCount', -1),
|
||||
removeBlocking: makeKeyIncrementer('blockingCount', -1),
|
||||
removePostingCount: makeKeyIncrementer('postingCount', -1),
|
||||
removeMentionedCount: makeKeyIncrementer('mentionedCount', -1),
|
||||
};
|
||||
|
||||
function makeKeyIncrementer(key: string, delta: number) {
|
||||
return async (name: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: { name },
|
||||
});
|
||||
|
||||
return (
|
||||
(result?.toJSON() as UserMetaModel) || {
|
||||
...emptyMeta,
|
||||
}
|
||||
);
|
||||
if (result) {
|
||||
const data = result.toJSON() as UserMetaModel;
|
||||
return result.update({
|
||||
...data,
|
||||
// @ts-ignore
|
||||
[key]: Math.max(0, (Number(data[key]) || 0) + delta),
|
||||
});
|
||||
}
|
||||
|
||||
const res = await model.create({
|
||||
name,
|
||||
...emptyMeta,
|
||||
[key]: Math.max(0, delta),
|
||||
});
|
||||
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
||||
const update = async (record: UserMetaModel) => {
|
||||
return model.create(record);
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
update,
|
||||
findOne,
|
||||
addFollower: makeKeyIncrementer('followerCount', 1),
|
||||
addFollowing: makeKeyIncrementer('followingCount', 1),
|
||||
addBlocked: makeKeyIncrementer('blockedCount', 1),
|
||||
addBlocking: makeKeyIncrementer('blockingCount', 1),
|
||||
addPostingCount: makeKeyIncrementer('postingCount', 1),
|
||||
addMentionedCount: makeKeyIncrementer('mentionedCount', 1),
|
||||
removeFollower: makeKeyIncrementer('followerCount', -1),
|
||||
removeFollowing: makeKeyIncrementer('followingCount', -1),
|
||||
removeBlocked: makeKeyIncrementer('blockedCount', -1),
|
||||
removeBlocking: makeKeyIncrementer('blockingCount', -1),
|
||||
removePostingCount: makeKeyIncrementer('postingCount', -1),
|
||||
removeMentionedCount: makeKeyIncrementer('mentionedCount', -1),
|
||||
};
|
||||
|
||||
function makeKeyIncrementer(key: string, delta: number) {
|
||||
return async (name: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: { name },
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const data = result.toJSON() as UserMetaModel;
|
||||
return result.update({
|
||||
...data,
|
||||
// @ts-ignore
|
||||
[key]: Math.max(0, (Number(data[key]) || 0) + delta),
|
||||
});
|
||||
}
|
||||
|
||||
const res = await model.create({
|
||||
name,
|
||||
...emptyMeta,
|
||||
[key]: Math.max(0, delta),
|
||||
});
|
||||
|
||||
return res;
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default userMeta;
|
||||
|
||||
@@ -3,137 +3,137 @@ import userMetaSeq from './userMeta';
|
||||
import { Mutex } from 'async-mutex';
|
||||
|
||||
export type UserModel = {
|
||||
username: string;
|
||||
type: 'ens' | 'arbitrum' | '';
|
||||
pubkey: string;
|
||||
joinedAt: number;
|
||||
joinedTx: string;
|
||||
name: string;
|
||||
bio: string;
|
||||
coverImage: string;
|
||||
profileImage: string;
|
||||
website: string;
|
||||
twitterVerification: string;
|
||||
group: boolean;
|
||||
meta: {
|
||||
inviteSent: string | null;
|
||||
acceptanceReceived: string | null;
|
||||
inviteReceived: string | null;
|
||||
acceptanceSent: string | null;
|
||||
blockedCount: number;
|
||||
blockingCount: number;
|
||||
followerCount: number;
|
||||
followingCount: number;
|
||||
postingCount: number;
|
||||
mentionedCount: number;
|
||||
followed: string | null;
|
||||
blocked: string | null;
|
||||
};
|
||||
username: string;
|
||||
type: 'ens' | 'arbitrum' | '';
|
||||
pubkey: string;
|
||||
joinedAt: number;
|
||||
joinedTx: string;
|
||||
name: string;
|
||||
bio: string;
|
||||
coverImage: string;
|
||||
profileImage: string;
|
||||
website: string;
|
||||
twitterVerification: string;
|
||||
group: boolean;
|
||||
meta: {
|
||||
inviteSent: string | null;
|
||||
acceptanceReceived: string | null;
|
||||
inviteReceived: string | null;
|
||||
acceptanceSent: string | null;
|
||||
blockedCount: number;
|
||||
blockingCount: number;
|
||||
followerCount: number;
|
||||
followingCount: number;
|
||||
postingCount: number;
|
||||
mentionedCount: number;
|
||||
followed: string | null;
|
||||
blocked: string | null;
|
||||
};
|
||||
};
|
||||
|
||||
const mutex = new Mutex();
|
||||
|
||||
const users = (sequelize: Sequelize) => {
|
||||
const model = sequelize.define(
|
||||
'users',
|
||||
{
|
||||
name: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
validate: {
|
||||
notEmpty: true,
|
||||
},
|
||||
primaryKey: true,
|
||||
unique: true,
|
||||
},
|
||||
pubkey: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
joinedAt: {
|
||||
type: BIGINT,
|
||||
},
|
||||
tx: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
type: {
|
||||
type: ENUM('arbitrum', 'ens', ''),
|
||||
allowNull: false,
|
||||
},
|
||||
const model = sequelize.define(
|
||||
'users',
|
||||
{
|
||||
name: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
validate: {
|
||||
notEmpty: true,
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['name'] },
|
||||
{ fields: ['pubkey'] },
|
||||
{ fields: ['type'] },
|
||||
{ fields: ['tx'] },
|
||||
],
|
||||
}
|
||||
);
|
||||
primaryKey: true,
|
||||
unique: true,
|
||||
},
|
||||
pubkey: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
joinedAt: {
|
||||
type: BIGINT,
|
||||
},
|
||||
tx: {
|
||||
type: STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
type: {
|
||||
type: ENUM('arbitrum', 'ens', ''),
|
||||
allowNull: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{ fields: ['name'] },
|
||||
{ fields: ['pubkey'] },
|
||||
{ fields: ['type'] },
|
||||
{ fields: ['tx'] },
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
const findOneByName = async (name: string, context = ''): Promise<UserModel | null> => {
|
||||
const values = await sequelize.query(
|
||||
`
|
||||
const findOneByName = async (name: string, context = ''): Promise<UserModel | null> => {
|
||||
const values = await sequelize.query(
|
||||
`
|
||||
${userSelectQuery}
|
||||
WHERE u.name = :name
|
||||
`,
|
||||
{
|
||||
type: QueryTypes.SELECT,
|
||||
replacements: {
|
||||
context: context || '',
|
||||
name: name,
|
||||
},
|
||||
}
|
||||
);
|
||||
{
|
||||
type: QueryTypes.SELECT,
|
||||
replacements: {
|
||||
context: context || '',
|
||||
name: name,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const [user] = inflateValuesToUserJSON(values);
|
||||
const [user] = inflateValuesToUserJSON(values);
|
||||
|
||||
return user || null;
|
||||
};
|
||||
return user || null;
|
||||
};
|
||||
|
||||
const findOneByPubkey = async (pubkey: string): Promise<UserModel | null> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
pubkey,
|
||||
},
|
||||
});
|
||||
const findOneByPubkey = async (pubkey: string): Promise<UserModel | null> => {
|
||||
let result = await model.findOne({
|
||||
where: {
|
||||
pubkey,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) return null;
|
||||
if (!result) return null;
|
||||
|
||||
const json = result.toJSON() as UserModel;
|
||||
const json = result.toJSON() as UserModel;
|
||||
|
||||
return json;
|
||||
};
|
||||
return json;
|
||||
};
|
||||
|
||||
const readAll = async (context = '', offset = 0, limit = 20): Promise<UserModel[]> => {
|
||||
const values = await sequelize.query(
|
||||
`
|
||||
const readAll = async (context = '', offset = 0, limit = 20): Promise<UserModel[]> => {
|
||||
const values = await sequelize.query(
|
||||
`
|
||||
${userSelectQuery}
|
||||
ORDER BY (umt."followerCount"+umt."postingCount"+umt."mentionedCount") ASC
|
||||
LIMIT :limit OFFSET :offset
|
||||
`,
|
||||
{
|
||||
type: QueryTypes.SELECT,
|
||||
replacements: {
|
||||
context: context || '',
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
}
|
||||
);
|
||||
{
|
||||
type: QueryTypes.SELECT,
|
||||
replacements: {
|
||||
context: context || '',
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return inflateValuesToUserJSON(values);
|
||||
};
|
||||
return inflateValuesToUserJSON(values);
|
||||
};
|
||||
|
||||
const search = async (
|
||||
query: string,
|
||||
context = '',
|
||||
offset = 0,
|
||||
limit = 5
|
||||
): Promise<UserModel[]> => {
|
||||
const values = await sequelize.query(
|
||||
`
|
||||
const search = async (
|
||||
query: string,
|
||||
context = '',
|
||||
offset = 0,
|
||||
limit = 5
|
||||
): Promise<UserModel[]> => {
|
||||
const values = await sequelize.query(
|
||||
`
|
||||
${userSelectQuery}
|
||||
WHERE (
|
||||
LOWER(u."name") LIKE :query
|
||||
@@ -142,75 +142,75 @@ const users = (sequelize: Sequelize) => {
|
||||
)
|
||||
LIMIT :limit OFFSET :offset
|
||||
`,
|
||||
{
|
||||
type: QueryTypes.SELECT,
|
||||
replacements: {
|
||||
context: context || '',
|
||||
query: `${query.toLowerCase()}%`,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
}
|
||||
);
|
||||
{
|
||||
type: QueryTypes.SELECT,
|
||||
replacements: {
|
||||
context: context || '',
|
||||
query: `${query.toLowerCase()}%`,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return inflateValuesToUserJSON(values);
|
||||
};
|
||||
return inflateValuesToUserJSON(values);
|
||||
};
|
||||
|
||||
const updateOrCreateUser = async (user: {
|
||||
name: string;
|
||||
pubkey: string;
|
||||
joinedAt: number;
|
||||
tx: string;
|
||||
type: 'ens' | 'arbitrum';
|
||||
}) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
name: user.name,
|
||||
},
|
||||
});
|
||||
const updateOrCreateUser = async (user: {
|
||||
name: string;
|
||||
pubkey: string;
|
||||
joinedAt: number;
|
||||
tx: string;
|
||||
type: 'ens' | 'arbitrum';
|
||||
}) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
name: user.name,
|
||||
},
|
||||
});
|
||||
|
||||
if (result) {
|
||||
const json = result.toJSON() as UserModel;
|
||||
if (user.joinedAt > Number(json.joinedAt)) {
|
||||
return result.update(user);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (result) {
|
||||
const json = result.toJSON() as UserModel;
|
||||
if (user.joinedAt > Number(json.joinedAt)) {
|
||||
return result.update(user);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return model.create(user);
|
||||
return model.create(user);
|
||||
});
|
||||
};
|
||||
|
||||
const ensureUser = async (name: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
name: name,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return model.create({
|
||||
name,
|
||||
tx: '',
|
||||
type: '',
|
||||
pubkey: '',
|
||||
joined: 0,
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const ensureUser = async (name: string) => {
|
||||
return mutex.runExclusive(async () => {
|
||||
const result = await model.findOne({
|
||||
where: {
|
||||
name: name,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return model.create({
|
||||
name,
|
||||
tx: '',
|
||||
type: '',
|
||||
pubkey: '',
|
||||
joined: 0,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
model,
|
||||
ensureUser,
|
||||
findOneByName,
|
||||
findOneByPubkey,
|
||||
readAll,
|
||||
search,
|
||||
updateOrCreateUser,
|
||||
};
|
||||
return {
|
||||
model,
|
||||
ensureUser,
|
||||
findOneByName,
|
||||
findOneByPubkey,
|
||||
readAll,
|
||||
search,
|
||||
updateOrCreateUser,
|
||||
};
|
||||
};
|
||||
|
||||
export default users;
|
||||
@@ -264,43 +264,43 @@ const userSelectQuery = `
|
||||
`;
|
||||
|
||||
function inflateValuesToUserJSON(values: any[]): UserModel[] {
|
||||
return values.map(value => {
|
||||
let twitterVerification = '';
|
||||
return values.map(value => {
|
||||
let twitterVerification = '';
|
||||
|
||||
if (value.tweetId && value.twitterHandle) {
|
||||
twitterVerification = `https://twitter.com/${value.twitterHandle}/status/${value.tweetId}`;
|
||||
}
|
||||
if (value.tweetId && value.twitterHandle) {
|
||||
twitterVerification = `https://twitter.com/${value.twitterHandle}/status/${value.tweetId}`;
|
||||
}
|
||||
|
||||
return {
|
||||
username: value.name,
|
||||
address: value.name,
|
||||
joinedTx: value.tx,
|
||||
type: value.type,
|
||||
pubkey: value.pubkey,
|
||||
joinedAt: Number(value.joinedAt),
|
||||
name: value.nickname || '',
|
||||
bio: value.bio || '',
|
||||
profileImage: value.profileImage || '',
|
||||
coverImage: value.coverImage || '',
|
||||
group: !!value.group,
|
||||
twitterVerification: twitterVerification,
|
||||
website: value.website || '',
|
||||
ecdh: value.ecdh || '',
|
||||
idcommitment: value.idcommitment || '',
|
||||
meta: {
|
||||
inviteSent: value.invited || null,
|
||||
acceptanceReceived: value.accepted || null,
|
||||
inviteReceived: value.invrecv || null,
|
||||
acceptanceSent: value.acceptsent || null,
|
||||
blockedCount: value.blockedCount ? Number(value.blockedCount) : 0,
|
||||
blockingCount: value.blockingCount ? Number(value.blockingCount) : 0,
|
||||
followerCount: value.followerCount ? Number(value.followerCount) : 0,
|
||||
followingCount: value.followingCount ? Number(value.followingCount) : 0,
|
||||
postingCount: value.postingCount ? Number(value.postingCount) : 0,
|
||||
mentionedCount: value.mentionedCount ? Number(value.mentionedCount) : 0,
|
||||
followed: value.followed,
|
||||
blocked: value.blocked,
|
||||
},
|
||||
};
|
||||
});
|
||||
return {
|
||||
username: value.name,
|
||||
address: value.name,
|
||||
joinedTx: value.tx,
|
||||
type: value.type,
|
||||
pubkey: value.pubkey,
|
||||
joinedAt: Number(value.joinedAt),
|
||||
name: value.nickname || '',
|
||||
bio: value.bio || '',
|
||||
profileImage: value.profileImage || '',
|
||||
coverImage: value.coverImage || '',
|
||||
group: !!value.group,
|
||||
twitterVerification: twitterVerification,
|
||||
website: value.website || '',
|
||||
ecdh: value.ecdh || '',
|
||||
idcommitment: value.idcommitment || '',
|
||||
meta: {
|
||||
inviteSent: value.invited || null,
|
||||
acceptanceReceived: value.accepted || null,
|
||||
inviteReceived: value.invrecv || null,
|
||||
acceptanceSent: value.acceptsent || null,
|
||||
blockedCount: value.blockedCount ? Number(value.blockedCount) : 0,
|
||||
blockingCount: value.blockingCount ? Number(value.blockingCount) : 0,
|
||||
followerCount: value.followerCount ? Number(value.followerCount) : 0,
|
||||
followingCount: value.followingCount ? Number(value.followingCount) : 0,
|
||||
postingCount: value.postingCount ? Number(value.postingCount) : 0,
|
||||
mentionedCount: value.mentionedCount ? Number(value.mentionedCount) : 0,
|
||||
followed: value.followed,
|
||||
blocked: value.blocked,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,69 +4,69 @@ import { stubCall } from '../util/testUtils';
|
||||
import ArbitrumService from './arbitrum';
|
||||
|
||||
tape('ArbitrumService', async t => {
|
||||
const arb = new ArbitrumService();
|
||||
const [call, stubs] = stubCall(arb);
|
||||
const arb = new ArbitrumService();
|
||||
const [call, stubs] = stubCall(arb);
|
||||
|
||||
t.equal(
|
||||
await arb.getNonce('0x5741cc1bDb03738Eaed6F227E435fc08e6bE157B'),
|
||||
'1',
|
||||
'get correct nonce'
|
||||
);
|
||||
t.equal(
|
||||
await arb.getNonce('0x5741cc1bDb03738Eaed6F227E435fc08e6bE157B'),
|
||||
'1',
|
||||
'get correct nonce'
|
||||
);
|
||||
|
||||
const updateFor = sinon.stub(arb.registrar.methods, 'updateFor').returns({ send: () => null });
|
||||
const updateFor = sinon.stub(arb.registrar.methods, 'updateFor').returns({ send: () => null });
|
||||
|
||||
const events: any[] = [
|
||||
{
|
||||
transactionHash: '',
|
||||
blockNumber: '',
|
||||
returnValues: {
|
||||
value: '0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161612e61616161616161616161616161616161616161616161616161616161616161616161616161616161616161',
|
||||
account: '0xmyuser',
|
||||
},
|
||||
},
|
||||
];
|
||||
const events: any[] = [
|
||||
{
|
||||
transactionHash: '',
|
||||
blockNumber: '',
|
||||
returnValues: {
|
||||
value:
|
||||
'0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161612e61616161616161616161616161616161616161616161616161616161616161616161616161616161616161',
|
||||
account: '0xmyuser',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
await arb.updateFor(
|
||||
'0x5741cc1bDb03738Eaed6F227E435fc08e6bE157B',
|
||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
||||
'0x12345'
|
||||
);
|
||||
await arb.updateFor(
|
||||
'0x5741cc1bDb03738Eaed6F227E435fc08e6bE157B',
|
||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
||||
'0x12345'
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
updateFor.args[0],
|
||||
[
|
||||
'0x5741cc1bDb03738Eaed6F227E435fc08e6bE157B',
|
||||
'0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161612e61616161616161616161616161616161616161616161616161616161616161616161616161616161616161',
|
||||
'0x12345',
|
||||
],
|
||||
'should send update with correct proof'
|
||||
);
|
||||
t.deepEqual(
|
||||
updateFor.args[0],
|
||||
[
|
||||
'0x5741cc1bDb03738Eaed6F227E435fc08e6bE157B',
|
||||
'0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161612e61616161616161616161616161616161616161616161616161616161616161616161616161616161616161',
|
||||
'0x12345',
|
||||
],
|
||||
'should send update with correct proof'
|
||||
);
|
||||
|
||||
stubs.app.read.returns(
|
||||
Promise.resolve({
|
||||
lastArbitrumBlockScanned: 2000000,
|
||||
})
|
||||
);
|
||||
stubs.app.read.returns(
|
||||
Promise.resolve({
|
||||
lastArbitrumBlockScanned: 2000000,
|
||||
})
|
||||
);
|
||||
|
||||
sinon
|
||||
.stub(arb.web3.eth, 'getTransaction')
|
||||
.returns(Promise.resolve({ hash: '0xtxhash' } as any));
|
||||
sinon.stub(arb.web3.eth, 'getBlock').returns(Promise.resolve({ timestamp: 1648624746 } as any));
|
||||
sinon.stub(arb.registrar, 'getPastEvents').returns(Promise.resolve(events));
|
||||
sinon.stub(arb.web3.eth, 'getTransaction').returns(Promise.resolve({ hash: '0xtxhash' } as any));
|
||||
sinon.stub(arb.web3.eth, 'getBlock').returns(Promise.resolve({ timestamp: 1648624746 } as any));
|
||||
sinon.stub(arb.registrar, 'getPastEvents').returns(Promise.resolve(events));
|
||||
|
||||
await arb.scanFromLast();
|
||||
await arb.scanFromLast();
|
||||
|
||||
t.deepEqual(stubs.users.updateOrCreateUser.args, [
|
||||
[
|
||||
{
|
||||
name: '0xmyuser',
|
||||
pubkey: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
||||
joinedAt: 1648624746000,
|
||||
tx: '0xtxhash',
|
||||
type: 'arbitrum',
|
||||
},
|
||||
],
|
||||
]);
|
||||
t.deepEqual(stubs.users.updateOrCreateUser.args, [
|
||||
[
|
||||
{
|
||||
name: '0xmyuser',
|
||||
pubkey:
|
||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
||||
joinedAt: 1648624746000,
|
||||
tx: '0xtxhash',
|
||||
type: 'arbitrum',
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
t.end();
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -7,126 +7,123 @@ import Timeout = NodeJS.Timeout;
|
||||
import { arbRegistrarABI } from '../util/abi';
|
||||
|
||||
export default class ArbitrumService extends GenericService {
|
||||
web3: Web3;
|
||||
registrar: Contract;
|
||||
scanTimeout?: Timeout | null;
|
||||
web3: Web3;
|
||||
registrar: Contract;
|
||||
scanTimeout?: Timeout | null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const httpProvider = new Web3.providers.HttpProvider(config.arbitrumHttpProvider);
|
||||
this.web3 = new Web3(httpProvider);
|
||||
this.web3.eth.accounts.wallet.add(config.arbitrumPrivateKey);
|
||||
this.registrar = new this.web3.eth.Contract(
|
||||
arbRegistrarABI as any,
|
||||
config.arbitrumRegistrar
|
||||
);
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
const httpProvider = new Web3.providers.HttpProvider(config.arbitrumHttpProvider);
|
||||
this.web3 = new Web3(httpProvider);
|
||||
this.web3.eth.accounts.wallet.add(config.arbitrumPrivateKey);
|
||||
this.registrar = new this.web3.eth.Contract(arbRegistrarABI as any, config.arbitrumRegistrar);
|
||||
}
|
||||
|
||||
getNonce = async (account: string): Promise<string> => {
|
||||
return this.registrar.methods.nonces(account).call();
|
||||
};
|
||||
getNonce = async (account: string): Promise<string> => {
|
||||
return this.registrar.methods.nonces(account).call();
|
||||
};
|
||||
|
||||
updateFor = async (account: string, pubkey: string, proof: string) => {
|
||||
const pubkeyBytes = this.web3.utils.utf8ToHex(pubkey);
|
||||
return await this.registrar.methods.updateFor(account, pubkeyBytes, proof).send({
|
||||
from: config.arbitrumAddress,
|
||||
gas: '10000000',
|
||||
});
|
||||
};
|
||||
updateFor = async (account: string, pubkey: string, proof: string) => {
|
||||
const pubkeyBytes = this.web3.utils.utf8ToHex(pubkey);
|
||||
return await this.registrar.methods.updateFor(account, pubkeyBytes, proof).send({
|
||||
from: config.arbitrumAddress,
|
||||
gas: '10000000',
|
||||
});
|
||||
};
|
||||
|
||||
async scanFromLast() {
|
||||
const app = await this.call('db', 'getApp');
|
||||
const data = await app.read();
|
||||
const lastBlock = data?.lastArbitrumBlockScanned;
|
||||
async scanFromLast() {
|
||||
const app = await this.call('db', 'getApp');
|
||||
const data = await app.read();
|
||||
const lastBlock = data?.lastArbitrumBlockScanned;
|
||||
|
||||
logger.info('scanning Autism Registrar on arbitrum', {
|
||||
logger.info('scanning Autism Registrar on arbitrum', {
|
||||
fromBlock: lastBlock,
|
||||
});
|
||||
|
||||
try {
|
||||
const block = await this.web3.eth.getBlock('latest');
|
||||
const toBlock = Math.min(block.number, lastBlock + 99999);
|
||||
|
||||
const events = await this.registrar.getPastEvents('RecordUpdatedFor', {
|
||||
fromBlock: lastBlock,
|
||||
toBlock: toBlock,
|
||||
});
|
||||
logger.info('scanned Autism Registrar on arbitrum', {
|
||||
fromBlock: lastBlock,
|
||||
toBlock: toBlock,
|
||||
});
|
||||
|
||||
for (let event of events) {
|
||||
const tx = await this.web3.eth.getTransaction(event.transactionHash);
|
||||
const block = await this.web3.eth.getBlock(event.blockNumber);
|
||||
|
||||
const pubkeyBytes = event.returnValues.value;
|
||||
const account = event.returnValues.account;
|
||||
const pubkey = Web3.utils.hexToUtf8(pubkeyBytes);
|
||||
|
||||
const x = pubkey.split('.')[0];
|
||||
const y = pubkey.split('.')[1];
|
||||
|
||||
if (x.length !== 43 || y.length !== 43) {
|
||||
logger.error('invalid pubkey', {
|
||||
fromBlock: lastBlock,
|
||||
});
|
||||
toBlock: block.number,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const users = await this.call('db', 'getUsers');
|
||||
|
||||
try {
|
||||
const block = await this.web3.eth.getBlock('latest');
|
||||
const toBlock = Math.min(block.number, lastBlock + 99999);
|
||||
|
||||
const events = await this.registrar.getPastEvents('RecordUpdatedFor', {
|
||||
fromBlock: lastBlock,
|
||||
toBlock: toBlock,
|
||||
});
|
||||
logger.info('scanned Autism Registrar on arbitrum', {
|
||||
fromBlock: lastBlock,
|
||||
toBlock: toBlock,
|
||||
});
|
||||
|
||||
for (let event of events) {
|
||||
const tx = await this.web3.eth.getTransaction(event.transactionHash);
|
||||
const block = await this.web3.eth.getBlock(event.blockNumber);
|
||||
|
||||
const pubkeyBytes = event.returnValues.value;
|
||||
const account = event.returnValues.account;
|
||||
const pubkey = Web3.utils.hexToUtf8(pubkeyBytes);
|
||||
|
||||
const x = pubkey.split('.')[0];
|
||||
const y = pubkey.split('.')[1];
|
||||
|
||||
if (x.length !== 43 || y.length !== 43) {
|
||||
logger.error('invalid pubkey', {
|
||||
fromBlock: lastBlock,
|
||||
toBlock: block.number,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const users = await this.call('db', 'getUsers');
|
||||
|
||||
try {
|
||||
await users.updateOrCreateUser({
|
||||
name: account,
|
||||
pubkey,
|
||||
joinedAt: Number(block.timestamp) * 1000,
|
||||
tx: tx.hash,
|
||||
type: 'arbitrum',
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error(e.message, {
|
||||
parent: e.parent,
|
||||
stack: e.stack,
|
||||
fromBlock: lastBlock,
|
||||
});
|
||||
}
|
||||
|
||||
await this.call('gun', 'watch', pubkey);
|
||||
|
||||
logger.info(`added pubkey for ${account}`, {
|
||||
transactionHash: tx.hash,
|
||||
blockNumber: tx.blockNumber,
|
||||
name: account,
|
||||
pubkey: pubkey,
|
||||
fromBlock: lastBlock,
|
||||
});
|
||||
}
|
||||
await app.updateLastArbitrumBlock(toBlock);
|
||||
|
||||
if (block.number > toBlock) return true;
|
||||
await users.updateOrCreateUser({
|
||||
name: account,
|
||||
pubkey,
|
||||
joinedAt: Number(block.timestamp) * 1000,
|
||||
tx: tx.hash,
|
||||
type: 'arbitrum',
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error(e.message, {
|
||||
parent: e.parent,
|
||||
stack: e.stack,
|
||||
fromBlock: lastBlock,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
scan = async () => {
|
||||
const shouldScanAgain = await this.scanFromLast();
|
||||
|
||||
if (this.scanTimeout) {
|
||||
clearTimeout(this.scanTimeout);
|
||||
this.scanTimeout = null;
|
||||
logger.error(e.message, {
|
||||
parent: e.parent,
|
||||
stack: e.stack,
|
||||
fromBlock: lastBlock,
|
||||
});
|
||||
}
|
||||
|
||||
this.scanTimeout = setTimeout(this.scan, shouldScanAgain ? 0 : 15000);
|
||||
};
|
||||
await this.call('gun', 'watch', pubkey);
|
||||
|
||||
async start() {
|
||||
this.scan();
|
||||
logger.info(`added pubkey for ${account}`, {
|
||||
transactionHash: tx.hash,
|
||||
blockNumber: tx.blockNumber,
|
||||
name: account,
|
||||
pubkey: pubkey,
|
||||
fromBlock: lastBlock,
|
||||
});
|
||||
}
|
||||
await app.updateLastArbitrumBlock(toBlock);
|
||||
|
||||
if (block.number > toBlock) return true;
|
||||
} catch (e) {
|
||||
logger.error(e.message, {
|
||||
parent: e.parent,
|
||||
stack: e.stack,
|
||||
fromBlock: lastBlock,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
scan = async () => {
|
||||
const shouldScanAgain = await this.scanFromLast();
|
||||
|
||||
if (this.scanTimeout) {
|
||||
clearTimeout(this.scanTimeout);
|
||||
this.scanTimeout = null;
|
||||
}
|
||||
|
||||
this.scanTimeout = setTimeout(this.scan, shouldScanAgain ? 0 : 15000);
|
||||
};
|
||||
|
||||
async start() {
|
||||
this.scan();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,247 +22,247 @@ import uploads from '../../models/uploads';
|
||||
import merkleRoot from '../../models/merkle_root';
|
||||
|
||||
export default class DBService extends GenericService {
|
||||
sequelize: Sequelize;
|
||||
sqlite: Sequelize;
|
||||
sequelize: Sequelize;
|
||||
sqlite: Sequelize;
|
||||
|
||||
app?: ReturnType<typeof app>;
|
||||
ens?: ReturnType<typeof ens>;
|
||||
linkPreview?: ReturnType<typeof linkPreview>;
|
||||
users?: ReturnType<typeof users>;
|
||||
records?: ReturnType<typeof records>;
|
||||
posts?: ReturnType<typeof posts>;
|
||||
profiles?: ReturnType<typeof profiles>;
|
||||
moderations?: ReturnType<typeof moderations>;
|
||||
connections?: ReturnType<typeof connections>;
|
||||
tags?: ReturnType<typeof tags>;
|
||||
semaphore?: ReturnType<typeof semaphore>;
|
||||
meta?: ReturnType<typeof meta>;
|
||||
userMeta?: ReturnType<typeof userMeta>;
|
||||
twitterAuth?: ReturnType<typeof twitterAuth>;
|
||||
interepGroups?: ReturnType<typeof interepGroups>;
|
||||
semaphoreCreators?: ReturnType<typeof semaphoreCreators>;
|
||||
threads?: ReturnType<typeof threads>;
|
||||
uploads?: ReturnType<typeof uploads>;
|
||||
merkleRoot?: ReturnType<typeof merkleRoot>;
|
||||
app?: ReturnType<typeof app>;
|
||||
ens?: ReturnType<typeof ens>;
|
||||
linkPreview?: ReturnType<typeof linkPreview>;
|
||||
users?: ReturnType<typeof users>;
|
||||
records?: ReturnType<typeof records>;
|
||||
posts?: ReturnType<typeof posts>;
|
||||
profiles?: ReturnType<typeof profiles>;
|
||||
moderations?: ReturnType<typeof moderations>;
|
||||
connections?: ReturnType<typeof connections>;
|
||||
tags?: ReturnType<typeof tags>;
|
||||
semaphore?: ReturnType<typeof semaphore>;
|
||||
meta?: ReturnType<typeof meta>;
|
||||
userMeta?: ReturnType<typeof userMeta>;
|
||||
twitterAuth?: ReturnType<typeof twitterAuth>;
|
||||
interepGroups?: ReturnType<typeof interepGroups>;
|
||||
semaphoreCreators?: ReturnType<typeof semaphoreCreators>;
|
||||
threads?: ReturnType<typeof threads>;
|
||||
uploads?: ReturnType<typeof uploads>;
|
||||
merkleRoot?: ReturnType<typeof merkleRoot>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.sqlite = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: process.env.NODE_ENV === 'test' ? './gun.test.db' : './gun.db',
|
||||
logging: false,
|
||||
});
|
||||
this.sqlite = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: process.env.NODE_ENV === 'test' ? './gun.test.db' : './gun.db',
|
||||
logging: false,
|
||||
});
|
||||
|
||||
if (!config.dbDialect || config.dbDialect === 'sqlite') {
|
||||
this.sequelize = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: config.dbStorage,
|
||||
logging: false,
|
||||
});
|
||||
} else {
|
||||
this.sequelize = new Sequelize(
|
||||
config.dbName as string,
|
||||
config.dbUsername as string,
|
||||
config.dbPassword,
|
||||
{
|
||||
host: config.dbHost,
|
||||
port: Number(config.dbPort),
|
||||
dialect: config.dbDialect as Dialect,
|
||||
logging: false,
|
||||
}
|
||||
);
|
||||
if (!config.dbDialect || config.dbDialect === 'sqlite') {
|
||||
this.sequelize = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: config.dbStorage,
|
||||
logging: false,
|
||||
});
|
||||
} else {
|
||||
this.sequelize = new Sequelize(
|
||||
config.dbName as string,
|
||||
config.dbUsername as string,
|
||||
config.dbPassword,
|
||||
{
|
||||
host: config.dbHost,
|
||||
port: Number(config.dbPort),
|
||||
dialect: config.dbDialect as Dialect,
|
||||
logging: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async getRecords(): Promise<ReturnType<typeof records>> {
|
||||
if (!this.records) {
|
||||
return Promise.reject(new Error('records is not initialized'));
|
||||
}
|
||||
return this.records;
|
||||
}
|
||||
|
||||
async getUsers(): Promise<ReturnType<typeof users>> {
|
||||
if (!this.users) {
|
||||
return Promise.reject(new Error('users is not initialized'));
|
||||
}
|
||||
return this.users;
|
||||
}
|
||||
|
||||
async getPosts(): Promise<ReturnType<typeof posts>> {
|
||||
if (!this.posts) {
|
||||
return Promise.reject(new Error('posts is not initialized'));
|
||||
}
|
||||
return this.posts;
|
||||
}
|
||||
|
||||
async getConnections(): Promise<ReturnType<typeof connections>> {
|
||||
if (!this.connections) {
|
||||
return Promise.reject(new Error('connections is not initialized'));
|
||||
}
|
||||
return this.connections;
|
||||
}
|
||||
|
||||
async getModerations(): Promise<ReturnType<typeof moderations>> {
|
||||
if (!this.moderations) {
|
||||
return Promise.reject(new Error('moderations is not initialized'));
|
||||
}
|
||||
return this.moderations;
|
||||
}
|
||||
|
||||
async getProfiles(): Promise<ReturnType<typeof profiles>> {
|
||||
if (!this.profiles) {
|
||||
return Promise.reject(new Error('profiles is not initialized'));
|
||||
}
|
||||
|
||||
async getRecords(): Promise<ReturnType<typeof records>> {
|
||||
if (!this.records) {
|
||||
return Promise.reject(new Error('records is not initialized'));
|
||||
}
|
||||
return this.records;
|
||||
return this.profiles;
|
||||
}
|
||||
|
||||
async getMeta(): Promise<ReturnType<typeof meta>> {
|
||||
if (!this.meta) {
|
||||
return Promise.reject(new Error('meta is not initialized'));
|
||||
}
|
||||
|
||||
async getUsers(): Promise<ReturnType<typeof users>> {
|
||||
if (!this.users) {
|
||||
return Promise.reject(new Error('users is not initialized'));
|
||||
}
|
||||
return this.users;
|
||||
return this.meta;
|
||||
}
|
||||
|
||||
async getTags(): Promise<ReturnType<typeof tags>> {
|
||||
if (!this.tags) {
|
||||
return Promise.reject(new Error('tags is not initialized'));
|
||||
}
|
||||
|
||||
async getPosts(): Promise<ReturnType<typeof posts>> {
|
||||
if (!this.posts) {
|
||||
return Promise.reject(new Error('posts is not initialized'));
|
||||
}
|
||||
return this.posts;
|
||||
return this.tags;
|
||||
}
|
||||
|
||||
async getUserMeta(): Promise<ReturnType<typeof userMeta>> {
|
||||
if (!this.userMeta) {
|
||||
return Promise.reject(new Error('userMeta is not initialized'));
|
||||
}
|
||||
|
||||
async getConnections(): Promise<ReturnType<typeof connections>> {
|
||||
if (!this.connections) {
|
||||
return Promise.reject(new Error('connections is not initialized'));
|
||||
}
|
||||
return this.connections;
|
||||
return this.userMeta;
|
||||
}
|
||||
|
||||
async getTwitterAuth(): Promise<ReturnType<typeof twitterAuth>> {
|
||||
if (!this.twitterAuth) {
|
||||
return Promise.reject(new Error('twitterAuth is not initialized'));
|
||||
}
|
||||
|
||||
async getModerations(): Promise<ReturnType<typeof moderations>> {
|
||||
if (!this.moderations) {
|
||||
return Promise.reject(new Error('moderations is not initialized'));
|
||||
}
|
||||
return this.moderations;
|
||||
return this.twitterAuth;
|
||||
}
|
||||
|
||||
async getApp(): Promise<ReturnType<typeof app>> {
|
||||
if (!this.app) {
|
||||
return Promise.reject(new Error('app is not initialized'));
|
||||
}
|
||||
return this.app;
|
||||
}
|
||||
|
||||
async getENS(): Promise<ReturnType<typeof ens>> {
|
||||
if (!this.ens) {
|
||||
return Promise.reject(new Error('ens is not initialized'));
|
||||
}
|
||||
return this.ens;
|
||||
}
|
||||
|
||||
async getLinkPreview(): Promise<ReturnType<typeof linkPreview>> {
|
||||
if (!this.linkPreview) {
|
||||
return Promise.reject(new Error('linkPreview is not initialized'));
|
||||
}
|
||||
return this.linkPreview;
|
||||
}
|
||||
|
||||
async getSemaphore(): Promise<ReturnType<typeof semaphore>> {
|
||||
if (!this.semaphore) {
|
||||
return Promise.reject(new Error('semaphore is not initialized'));
|
||||
}
|
||||
return this.semaphore;
|
||||
}
|
||||
|
||||
async getInterepGroups(): Promise<ReturnType<typeof interepGroups>> {
|
||||
if (!this.interepGroups) {
|
||||
return Promise.reject(new Error('interepGroups is not initialized'));
|
||||
}
|
||||
return this.interepGroups;
|
||||
}
|
||||
|
||||
async getSemaphoreCreators(): Promise<ReturnType<typeof semaphoreCreators>> {
|
||||
if (!this.semaphoreCreators) {
|
||||
return Promise.reject(new Error('semaphoreCreators is not initialized'));
|
||||
}
|
||||
return this.semaphoreCreators;
|
||||
}
|
||||
|
||||
async getThreads(): Promise<ReturnType<typeof threads>> {
|
||||
if (!this.threads) {
|
||||
return Promise.reject(new Error('threads is not initialized'));
|
||||
}
|
||||
return this.threads;
|
||||
}
|
||||
|
||||
async getUploads(): Promise<ReturnType<typeof uploads>> {
|
||||
if (!this.uploads) {
|
||||
return Promise.reject(new Error('uploads is not initialized'));
|
||||
}
|
||||
return this.uploads;
|
||||
}
|
||||
|
||||
async start() {
|
||||
this.app = app(this.sqlite);
|
||||
this.records = records(this.sqlite);
|
||||
this.linkPreview = linkPreview(this.sequelize);
|
||||
this.meta = meta(this.sequelize);
|
||||
this.userMeta = userMeta(this.sequelize);
|
||||
this.moderations = moderations(this.sequelize);
|
||||
this.connections = connections(this.sequelize);
|
||||
this.users = users(this.sequelize);
|
||||
this.posts = posts(this.sequelize);
|
||||
this.tags = tags(this.sequelize);
|
||||
this.profiles = profiles(this.sequelize);
|
||||
this.semaphore = semaphore(this.sequelize);
|
||||
this.ens = ens(this.sequelize);
|
||||
this.twitterAuth = twitterAuth(this.sequelize);
|
||||
this.interepGroups = interepGroups(this.sequelize);
|
||||
this.semaphoreCreators = semaphoreCreators(this.sequelize);
|
||||
this.threads = threads(this.sequelize);
|
||||
this.uploads = uploads(this.sequelize);
|
||||
this.merkleRoot = merkleRoot(this.sequelize);
|
||||
|
||||
await this.app?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.linkPreview?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.records?.model.sync({ force: !!process.env.FORCE });
|
||||
|
||||
await this.semaphore?.model.sync({ force: !!process.env.FORCE });
|
||||
|
||||
await this.users?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.moderations?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.connections?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.profiles?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.posts?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.tags?.model.sync({ force: !!process.env.FORCE });
|
||||
|
||||
await this.userMeta?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.meta?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.ens?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.twitterAuth?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.interepGroups?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.semaphoreCreators?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.threads?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.uploads?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.merkleRoot?.model.sync({ force: !!process.env.FORCE });
|
||||
|
||||
const appData = await this.app?.read();
|
||||
|
||||
if (!appData) {
|
||||
await this.app?.updateLastENSBlock(12957300);
|
||||
await this.app?.updateLastInterrepBlock(28311377);
|
||||
await this.app?.updateLastArbitrumBlock(2193241);
|
||||
await this.app?.updateLastGroup42BlockScanned(7660170);
|
||||
// await this.app?.updateLastArbitrumBlock(9317700);
|
||||
}
|
||||
|
||||
async getProfiles(): Promise<ReturnType<typeof profiles>> {
|
||||
if (!this.profiles) {
|
||||
return Promise.reject(new Error('profiles is not initialized'));
|
||||
}
|
||||
await this.app?.updateLastGroup42BlockScanned(7660170);
|
||||
|
||||
return this.profiles;
|
||||
}
|
||||
|
||||
async getMeta(): Promise<ReturnType<typeof meta>> {
|
||||
if (!this.meta) {
|
||||
return Promise.reject(new Error('meta is not initialized'));
|
||||
}
|
||||
|
||||
return this.meta;
|
||||
}
|
||||
|
||||
async getTags(): Promise<ReturnType<typeof tags>> {
|
||||
if (!this.tags) {
|
||||
return Promise.reject(new Error('tags is not initialized'));
|
||||
}
|
||||
|
||||
return this.tags;
|
||||
}
|
||||
|
||||
async getUserMeta(): Promise<ReturnType<typeof userMeta>> {
|
||||
if (!this.userMeta) {
|
||||
return Promise.reject(new Error('userMeta is not initialized'));
|
||||
}
|
||||
|
||||
return this.userMeta;
|
||||
}
|
||||
|
||||
async getTwitterAuth(): Promise<ReturnType<typeof twitterAuth>> {
|
||||
if (!this.twitterAuth) {
|
||||
return Promise.reject(new Error('twitterAuth is not initialized'));
|
||||
}
|
||||
|
||||
return this.twitterAuth;
|
||||
}
|
||||
|
||||
async getApp(): Promise<ReturnType<typeof app>> {
|
||||
if (!this.app) {
|
||||
return Promise.reject(new Error('app is not initialized'));
|
||||
}
|
||||
return this.app;
|
||||
}
|
||||
|
||||
async getENS(): Promise<ReturnType<typeof ens>> {
|
||||
if (!this.ens) {
|
||||
return Promise.reject(new Error('ens is not initialized'));
|
||||
}
|
||||
return this.ens;
|
||||
}
|
||||
|
||||
async getLinkPreview(): Promise<ReturnType<typeof linkPreview>> {
|
||||
if (!this.linkPreview) {
|
||||
return Promise.reject(new Error('linkPreview is not initialized'));
|
||||
}
|
||||
return this.linkPreview;
|
||||
}
|
||||
|
||||
async getSemaphore(): Promise<ReturnType<typeof semaphore>> {
|
||||
if (!this.semaphore) {
|
||||
return Promise.reject(new Error('semaphore is not initialized'));
|
||||
}
|
||||
return this.semaphore;
|
||||
}
|
||||
|
||||
async getInterepGroups(): Promise<ReturnType<typeof interepGroups>> {
|
||||
if (!this.interepGroups) {
|
||||
return Promise.reject(new Error('interepGroups is not initialized'));
|
||||
}
|
||||
return this.interepGroups;
|
||||
}
|
||||
|
||||
async getSemaphoreCreators(): Promise<ReturnType<typeof semaphoreCreators>> {
|
||||
if (!this.semaphoreCreators) {
|
||||
return Promise.reject(new Error('semaphoreCreators is not initialized'));
|
||||
}
|
||||
return this.semaphoreCreators;
|
||||
}
|
||||
|
||||
async getThreads(): Promise<ReturnType<typeof threads>> {
|
||||
if (!this.threads) {
|
||||
return Promise.reject(new Error('threads is not initialized'));
|
||||
}
|
||||
return this.threads;
|
||||
}
|
||||
|
||||
async getUploads(): Promise<ReturnType<typeof uploads>> {
|
||||
if (!this.uploads) {
|
||||
return Promise.reject(new Error('uploads is not initialized'));
|
||||
}
|
||||
return this.uploads;
|
||||
}
|
||||
|
||||
async start() {
|
||||
this.app = app(this.sqlite);
|
||||
this.records = records(this.sqlite);
|
||||
this.linkPreview = linkPreview(this.sequelize);
|
||||
this.meta = meta(this.sequelize);
|
||||
this.userMeta = userMeta(this.sequelize);
|
||||
this.moderations = moderations(this.sequelize);
|
||||
this.connections = connections(this.sequelize);
|
||||
this.users = users(this.sequelize);
|
||||
this.posts = posts(this.sequelize);
|
||||
this.tags = tags(this.sequelize);
|
||||
this.profiles = profiles(this.sequelize);
|
||||
this.semaphore = semaphore(this.sequelize);
|
||||
this.ens = ens(this.sequelize);
|
||||
this.twitterAuth = twitterAuth(this.sequelize);
|
||||
this.interepGroups = interepGroups(this.sequelize);
|
||||
this.semaphoreCreators = semaphoreCreators(this.sequelize);
|
||||
this.threads = threads(this.sequelize);
|
||||
this.uploads = uploads(this.sequelize);
|
||||
this.merkleRoot = merkleRoot(this.sequelize);
|
||||
|
||||
await this.app?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.linkPreview?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.records?.model.sync({ force: !!process.env.FORCE });
|
||||
|
||||
await this.semaphore?.model.sync({ force: !!process.env.FORCE });
|
||||
|
||||
await this.users?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.moderations?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.connections?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.profiles?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.posts?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.tags?.model.sync({ force: !!process.env.FORCE });
|
||||
|
||||
await this.userMeta?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.meta?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.ens?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.twitterAuth?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.interepGroups?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.semaphoreCreators?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.threads?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.uploads?.model.sync({ force: !!process.env.FORCE });
|
||||
await this.merkleRoot?.model.sync({ force: !!process.env.FORCE });
|
||||
|
||||
const appData = await this.app?.read();
|
||||
|
||||
if (!appData) {
|
||||
await this.app?.updateLastENSBlock(12957300);
|
||||
await this.app?.updateLastInterrepBlock(28311377);
|
||||
await this.app?.updateLastArbitrumBlock(2193241);
|
||||
await this.app?.updateLastGroup42BlockScanned(7660170);
|
||||
// await this.app?.updateLastArbitrumBlock(9317700);
|
||||
}
|
||||
|
||||
await this.app?.updateLastGroup42BlockScanned(7660170);
|
||||
|
||||
// await this.app?.updateLastArbitrumBlock(2193241);
|
||||
// await this.app?.updateLastInterrepBlock(27202837);
|
||||
}
|
||||
// await this.app?.updateLastArbitrumBlock(2193241);
|
||||
// await this.app?.updateLastInterrepBlock(27202837);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,39 +4,39 @@ import ENSService from './ens';
|
||||
import { stubCall } from '../util/testUtils';
|
||||
|
||||
tape('ENSService', async t => {
|
||||
const ens = new ENSService();
|
||||
const [call, stubs] = stubCall(ens);
|
||||
await ens.start();
|
||||
const ens = new ENSService();
|
||||
const [call, stubs] = stubCall(ens);
|
||||
await ens.start();
|
||||
|
||||
t.equal(
|
||||
await ens.fetchAddressByName('yagamilight.eth'),
|
||||
'0xd44a82dD160217d46D754a03C8f841edF06EBE3c',
|
||||
'should get address for yagamilight.eth'
|
||||
);
|
||||
t.equal(
|
||||
await ens.fetchAddressByName('yagamilight.eth'),
|
||||
'0xd44a82dD160217d46D754a03C8f841edF06EBE3c',
|
||||
'should get address for yagamilight.eth'
|
||||
);
|
||||
|
||||
t.equal(
|
||||
await ens.fetchAddressByName('0xd44a82dD160217d46D754a03C8f841edF06EBE3c'),
|
||||
'0xd44a82dD160217d46D754a03C8f841edF06EBE3c',
|
||||
'should get address for 0xd44a82dD160217d46D754a03C8f841edF06EBE3c'
|
||||
);
|
||||
t.equal(
|
||||
await ens.fetchAddressByName('0xd44a82dD160217d46D754a03C8f841edF06EBE3c'),
|
||||
'0xd44a82dD160217d46D754a03C8f841edF06EBE3c',
|
||||
'should get address for 0xd44a82dD160217d46D754a03C8f841edF06EBE3c'
|
||||
);
|
||||
|
||||
t.equal(
|
||||
await ens.fetchNameByAddress('0xd44a82dD160217d46D754a03C8f841edF06EBE3c'),
|
||||
'yagamilight.eth',
|
||||
'should get ens for 0xd44a82dD160217d46D754a03C8f841edF06EBE3c'
|
||||
);
|
||||
t.equal(
|
||||
await ens.fetchNameByAddress('0xd44a82dD160217d46D754a03C8f841edF06EBE3c'),
|
||||
'yagamilight.eth',
|
||||
'should get ens for 0xd44a82dD160217d46D754a03C8f841edF06EBE3c'
|
||||
);
|
||||
|
||||
t.equal(
|
||||
await ens.fetchNameByAddress('0xd44a82dD160217d46D754a03C8f841edF06EBE3d'),
|
||||
null,
|
||||
'should get ens for 0xd44a82dD160217d46D754a03C8f841edF06EBE3d'
|
||||
);
|
||||
t.equal(
|
||||
await ens.fetchNameByAddress('0xd44a82dD160217d46D754a03C8f841edF06EBE3d'),
|
||||
null,
|
||||
'should get ens for 0xd44a82dD160217d46D754a03C8f841edF06EBE3d'
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
stubs.ens.update.args[0],
|
||||
['yagamilight.eth', '0xd44a82dD160217d46D754a03C8f841edF06EBE3c'],
|
||||
'shuld update ens database'
|
||||
);
|
||||
t.deepEqual(
|
||||
stubs.ens.update.args[0],
|
||||
['yagamilight.eth', '0xd44a82dD160217d46D754a03C8f841edF06EBE3c'],
|
||||
'shuld update ens database'
|
||||
);
|
||||
|
||||
t.end();
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -9,65 +9,65 @@ import Timeout = NodeJS.Timeout;
|
||||
const { default: ENS, getEnsAddress } = require('@ensdomains/ensjs');
|
||||
|
||||
const cache = new LRU({
|
||||
max: 1000,
|
||||
maxAge: 60 * 60 * 1000,
|
||||
max: 1000,
|
||||
maxAge: 60 * 60 * 1000,
|
||||
});
|
||||
|
||||
export default class ENSService extends GenericService {
|
||||
web3: Web3;
|
||||
resolver: Contract;
|
||||
ens: typeof ENS;
|
||||
scanTimeout?: Timeout | null;
|
||||
web3: Web3;
|
||||
resolver: Contract;
|
||||
ens: typeof ENS;
|
||||
scanTimeout?: Timeout | null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const httpProvider = new Web3.providers.HttpProvider(config.web3HttpProvider);
|
||||
this.web3 = new Web3(httpProvider);
|
||||
this.resolver = new this.web3.eth.Contract(ensResolverABI as any, config.ensResolver);
|
||||
this.ens = new ENS({
|
||||
provider: httpProvider,
|
||||
ensAddress: getEnsAddress('1'),
|
||||
});
|
||||
constructor() {
|
||||
super();
|
||||
const httpProvider = new Web3.providers.HttpProvider(config.web3HttpProvider);
|
||||
this.web3 = new Web3(httpProvider);
|
||||
this.resolver = new this.web3.eth.Contract(ensResolverABI as any, config.ensResolver);
|
||||
this.ens = new ENS({
|
||||
provider: httpProvider,
|
||||
ensAddress: getEnsAddress('1'),
|
||||
});
|
||||
}
|
||||
|
||||
ecrecover = async (data: string, sig: string) => {
|
||||
return this.web3.eth.accounts.recover(data, sig);
|
||||
};
|
||||
|
||||
fetchNameByAddress = async (address: string) => {
|
||||
const cached = cache.get(address);
|
||||
|
||||
if (cache.get(address)) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
ecrecover = async (data: string, sig: string) => {
|
||||
return this.web3.eth.accounts.recover(data, sig);
|
||||
};
|
||||
const { name } = await this.ens.getName(address);
|
||||
|
||||
fetchNameByAddress = async (address: string) => {
|
||||
const cached = cache.get(address);
|
||||
if (!name) return null;
|
||||
|
||||
if (cache.get(address)) {
|
||||
return cached;
|
||||
}
|
||||
cache.set(address, name);
|
||||
const ens = await this.call('db', 'getENS');
|
||||
await ens.update(name, address);
|
||||
return name;
|
||||
};
|
||||
|
||||
const { name } = await this.ens.getName(address);
|
||||
fetchAddressByName = async (name: string) => {
|
||||
if (Web3.utils.isAddress(name)) return name;
|
||||
|
||||
if (!name) return null;
|
||||
const cached = cache.get(name);
|
||||
|
||||
cache.set(address, name);
|
||||
const ens = await this.call('db', 'getENS');
|
||||
await ens.update(name, address);
|
||||
return name;
|
||||
};
|
||||
if (cache.get(name)) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
fetchAddressByName = async (name: string) => {
|
||||
if (Web3.utils.isAddress(name)) return name;
|
||||
const address = await this.ens.name(name).getAddress();
|
||||
|
||||
const cached = cache.get(name);
|
||||
if (!address) throw new Error(`cannot find address for ${name}`);
|
||||
|
||||
if (cache.get(name)) {
|
||||
return cached;
|
||||
}
|
||||
cache.set(name, address);
|
||||
const ens = await this.call('db', 'getENS');
|
||||
await ens.update(name, address);
|
||||
|
||||
const address = await this.ens.name(name).getAddress();
|
||||
|
||||
if (!address) throw new Error(`cannot find address for ${name}`);
|
||||
|
||||
cache.set(name, address);
|
||||
const ens = await this.call('db', 'getENS');
|
||||
await ens.update(name, address);
|
||||
|
||||
return address;
|
||||
};
|
||||
return address;
|
||||
};
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1388
src/services/gun.ts
1388
src/services/gun.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2017
src/services/http.ts
2017
src/services/http.ts
File diff suppressed because it is too large
Load Diff
@@ -11,69 +11,69 @@ const interrep = new InterrepService();
|
||||
const [callStub, { sempahore, interepGroups }] = stubCall(interrep);
|
||||
|
||||
tape('InterepService.start', async (t: any) => {
|
||||
const datas = [
|
||||
[
|
||||
{
|
||||
provider: 'twitter',
|
||||
name: 'gold',
|
||||
depth: 20,
|
||||
root: '19217088683336594659449020493828377907203207941212636669271704950158751593251',
|
||||
numberOfLeaves: 0,
|
||||
size: 0,
|
||||
},
|
||||
{
|
||||
provider: 'poap',
|
||||
name: 'devcon5',
|
||||
depth: 20,
|
||||
root: '19217088683336594659449020493828377907203207941212636669271704950158751593251',
|
||||
numberOfLeaves: 0,
|
||||
size: 0,
|
||||
},
|
||||
{
|
||||
provider: 'telegram',
|
||||
name: '-1001396261340',
|
||||
depth: 20,
|
||||
root: '19217088683336594659449020493828377907203207941212636669271704950158751593251',
|
||||
numberOfLeaves: 0,
|
||||
size: 0,
|
||||
},
|
||||
{
|
||||
provider: 'email',
|
||||
name: 'hotmail',
|
||||
depth: 20,
|
||||
root: '19217088683336594659449020493828377907203207941212636669271704950158751593251',
|
||||
numberOfLeaves: 0,
|
||||
size: 0,
|
||||
},
|
||||
],
|
||||
];
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
json: async () => {
|
||||
return {
|
||||
data: datas.shift() || [],
|
||||
};
|
||||
},
|
||||
})
|
||||
);
|
||||
const datas = [
|
||||
[
|
||||
{
|
||||
provider: 'twitter',
|
||||
name: 'gold',
|
||||
depth: 20,
|
||||
root: '19217088683336594659449020493828377907203207941212636669271704950158751593251',
|
||||
numberOfLeaves: 0,
|
||||
size: 0,
|
||||
},
|
||||
{
|
||||
provider: 'poap',
|
||||
name: 'devcon5',
|
||||
depth: 20,
|
||||
root: '19217088683336594659449020493828377907203207941212636669271704950158751593251',
|
||||
numberOfLeaves: 0,
|
||||
size: 0,
|
||||
},
|
||||
{
|
||||
provider: 'telegram',
|
||||
name: '-1001396261340',
|
||||
depth: 20,
|
||||
root: '19217088683336594659449020493828377907203207941212636669271704950158751593251',
|
||||
numberOfLeaves: 0,
|
||||
size: 0,
|
||||
},
|
||||
{
|
||||
provider: 'email',
|
||||
name: 'hotmail',
|
||||
depth: 20,
|
||||
root: '19217088683336594659449020493828377907203207941212636669271704950158751593251',
|
||||
numberOfLeaves: 0,
|
||||
size: 0,
|
||||
},
|
||||
],
|
||||
];
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
json: async () => {
|
||||
return {
|
||||
data: datas.shift() || [],
|
||||
};
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
await interrep.start();
|
||||
await interrep.start();
|
||||
|
||||
t.equal(
|
||||
fetchStub.args[0][0],
|
||||
'https://kovan.interep.link/api/v1/groups',
|
||||
'should fetch groups data from interep API on start'
|
||||
);
|
||||
t.equal(
|
||||
fetchStub.args[0][0],
|
||||
'https://kovan.interep.link/api/v1/groups',
|
||||
'should fetch groups data from interep API on start'
|
||||
);
|
||||
|
||||
t.equal(
|
||||
fetchStub.args[1][0],
|
||||
'https://kovan.interep.link/api/v1/groups/twitter/gold/members?limit=1000&offset=0',
|
||||
'should fetch groups membership data from interep API on start'
|
||||
);
|
||||
t.equal(
|
||||
fetchStub.args[1][0],
|
||||
'https://kovan.interep.link/api/v1/groups/twitter/gold/members?limit=1000&offset=0',
|
||||
'should fetch groups membership data from interep API on start'
|
||||
);
|
||||
|
||||
fetchStub.reset();
|
||||
fetchStub.reset();
|
||||
|
||||
t.end();
|
||||
t.end();
|
||||
});
|
||||
|
||||
// tape('InterepService.addId', async t => {
|
||||
@@ -102,89 +102,89 @@ tape('InterepService.start', async (t: any) => {
|
||||
// });
|
||||
|
||||
tape('InterepService.getBatchFromRootHash', async t => {
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
json: async () => ({
|
||||
data: {
|
||||
group: {
|
||||
provider: 'autism',
|
||||
name: 'diamond',
|
||||
},
|
||||
},
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const res = await interrep.getBatchFromRootHash('0x123456');
|
||||
|
||||
t.same(interepGroups.findOneByHash.args[0], ['0x123456']);
|
||||
|
||||
console.log(fetchStub.args[1]);
|
||||
t.same(fetchStub.args[0][0], 'https://kovan.interep.link/api/v1/batches/0x123456');
|
||||
|
||||
t.same(
|
||||
res,
|
||||
{
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
json: async () => ({
|
||||
data: {
|
||||
group: {
|
||||
provider: 'autism',
|
||||
name: 'diamond',
|
||||
root_hash: '0x123456',
|
||||
},
|
||||
},
|
||||
'should return batch from interep'
|
||||
);
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
fetchStub.reset();
|
||||
interepGroups.findOneByHash.reset();
|
||||
const res = await interrep.getBatchFromRootHash('0x123456');
|
||||
|
||||
t.end();
|
||||
t.same(interepGroups.findOneByHash.args[0], ['0x123456']);
|
||||
|
||||
console.log(fetchStub.args[1]);
|
||||
t.same(fetchStub.args[0][0], 'https://kovan.interep.link/api/v1/batches/0x123456');
|
||||
|
||||
t.same(
|
||||
res,
|
||||
{
|
||||
provider: 'autism',
|
||||
name: 'diamond',
|
||||
root_hash: '0x123456',
|
||||
},
|
||||
'should return batch from interep'
|
||||
);
|
||||
|
||||
fetchStub.reset();
|
||||
interepGroups.findOneByHash.reset();
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
tape('InterepService.getProofFromGroup', async t => {
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
json: async () => ({
|
||||
data: 'apple',
|
||||
}),
|
||||
})
|
||||
);
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
json: async () => ({
|
||||
data: 'apple',
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const res = await interrep.getProofFromGroup('0x123456', 'autism', 'diamond');
|
||||
const res = await interrep.getProofFromGroup('0x123456', 'autism', 'diamond');
|
||||
|
||||
t.same(
|
||||
fetchStub.args[0][0],
|
||||
'https://kovan.interep.link/api/v1/groups/0x123456/autism/diamond/proof'
|
||||
);
|
||||
t.same(
|
||||
fetchStub.args[0][0],
|
||||
'https://kovan.interep.link/api/v1/groups/0x123456/autism/diamond/proof'
|
||||
);
|
||||
|
||||
t.same(
|
||||
res,
|
||||
{
|
||||
data: 'apple',
|
||||
},
|
||||
'should return batch from interep'
|
||||
);
|
||||
t.same(
|
||||
res,
|
||||
{
|
||||
data: 'apple',
|
||||
},
|
||||
'should return batch from interep'
|
||||
);
|
||||
|
||||
fetchStub.reset();
|
||||
fetchStub.reset();
|
||||
|
||||
t.end();
|
||||
t.end();
|
||||
});
|
||||
|
||||
tape('InterepService.inProvider', async t => {
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
json: async () => ({
|
||||
data: 'apple',
|
||||
}),
|
||||
})
|
||||
);
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
json: async () => ({
|
||||
data: 'apple',
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const res = await interrep.inProvider('autism', '0x123456');
|
||||
const res = await interrep.inProvider('autism', '0x123456');
|
||||
|
||||
t.same(fetchStub.args[0][0], 'https://kovan.interep.link/api/v1/providers/autism/0x123456');
|
||||
t.same(fetchStub.args[0][0], 'https://kovan.interep.link/api/v1/providers/autism/0x123456');
|
||||
|
||||
t.equal(res, true);
|
||||
t.equal(res, true);
|
||||
|
||||
fetchStub.reset();
|
||||
fetchStub.reset();
|
||||
|
||||
t.end();
|
||||
t.end();
|
||||
});
|
||||
|
||||
// tape('InterepService.scanIDCommitment', async t => {
|
||||
|
||||
@@ -5,155 +5,155 @@ import { sequelize } from '../util/sequelize';
|
||||
import semaphore from '../models/semaphore';
|
||||
|
||||
export type InterepGroup = {
|
||||
provider: 'twitter' | 'github' | 'reddit';
|
||||
name: string;
|
||||
root: string;
|
||||
size: number;
|
||||
provider: 'twitter' | 'github' | 'reddit';
|
||||
name: string;
|
||||
root: string;
|
||||
size: number;
|
||||
};
|
||||
|
||||
const INTEREP_SYNC_INTERVAL = 15 * 60 * 1000;
|
||||
|
||||
export default class InterrepService extends GenericService {
|
||||
interepGroups?: ReturnType<typeof interepGroups>;
|
||||
semaphore?: ReturnType<typeof semaphore>;
|
||||
interepGroups?: ReturnType<typeof interepGroups>;
|
||||
semaphore?: ReturnType<typeof semaphore>;
|
||||
|
||||
groups: {
|
||||
[providerName: string]: InterepGroup[];
|
||||
};
|
||||
groups: {
|
||||
[providerName: string]: InterepGroup[];
|
||||
};
|
||||
|
||||
providers = ['twitter', 'github', 'reddit'];
|
||||
providers = ['twitter', 'github', 'reddit'];
|
||||
|
||||
timeout: any;
|
||||
timeout: any;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.groups = {};
|
||||
constructor() {
|
||||
super();
|
||||
this.groups = {};
|
||||
}
|
||||
|
||||
syncOne = async (group: string) => {
|
||||
const [protocol, groupType, groupName] = group.split('_');
|
||||
// @ts-ignore
|
||||
const resp = await fetch(`${config.interrepAPI}/api/v1/groups/${groupType}/${groupName}`);
|
||||
const json = await resp.json();
|
||||
const data = json.data;
|
||||
const existing = await this.interepGroups!.getGroup(groupType, groupName);
|
||||
if (existing?.root_hash !== data.root) {
|
||||
await this.fetchMembersFromGroup(data.root, groupType, groupName);
|
||||
await this.interepGroups?.addHash(data.root, groupType, groupName);
|
||||
}
|
||||
};
|
||||
|
||||
sync = async () => {
|
||||
try {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
// @ts-ignore
|
||||
const resp = await fetch(`${config.interrepAPI}/api/v1/groups`);
|
||||
const json = await resp.json();
|
||||
const groups = json.data;
|
||||
if (groups?.length) {
|
||||
for (let group of groups) {
|
||||
const { root, provider, name } = group;
|
||||
const existing = await this.interepGroups!.getGroup(provider, name);
|
||||
if (existing?.root_hash !== root) {
|
||||
await this.fetchMembersFromGroup(root, provider, name);
|
||||
await this.interepGroups?.addHash(root, provider, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
this.timeout = setTimeout(this.sync, INTEREP_SYNC_INTERVAL);
|
||||
}
|
||||
};
|
||||
|
||||
async fetchMembersFromGroup(
|
||||
root: string,
|
||||
provider: string,
|
||||
name: string,
|
||||
limit = 1000,
|
||||
offset = 0
|
||||
): Promise<void> {
|
||||
const groupId = `interrep_${provider}_${name}`;
|
||||
// @ts-ignore
|
||||
const resp = await fetch(
|
||||
`${config.interrepAPI}/api/v1/groups/${provider}/${name}/members?limit=${limit}&offset=${offset}`
|
||||
);
|
||||
const json = await resp.json();
|
||||
const members: string[] = json.data;
|
||||
if (members.length) {
|
||||
for (const member of members) {
|
||||
await this.semaphore?.addID(BigInt(member).toString(16), groupId, root);
|
||||
}
|
||||
}
|
||||
|
||||
syncOne = async (group: string) => {
|
||||
const [protocol, groupType, groupName] = group.split('_');
|
||||
// @ts-ignore
|
||||
const resp = await fetch(`${config.interrepAPI}/api/v1/groups/${groupType}/${groupName}`);
|
||||
const json = await resp.json();
|
||||
const data = json.data;
|
||||
const existing = await this.interepGroups!.getGroup(groupType, groupName);
|
||||
if (existing?.root_hash !== data.root) {
|
||||
await this.fetchMembersFromGroup(data.root, groupType, groupName);
|
||||
await this.interepGroups?.addHash(data.root, groupType, groupName);
|
||||
}
|
||||
};
|
||||
if (members.length >= limit) {
|
||||
await this.fetchMembersFromGroup(root, provider, name, limit + 1000, limit);
|
||||
}
|
||||
}
|
||||
|
||||
sync = async () => {
|
||||
try {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
// @ts-ignore
|
||||
const resp = await fetch(`${config.interrepAPI}/api/v1/groups`);
|
||||
const json = await resp.json();
|
||||
const groups = json.data;
|
||||
if (groups?.length) {
|
||||
for (let group of groups) {
|
||||
const { root, provider, name } = group;
|
||||
const existing = await this.interepGroups!.getGroup(provider, name);
|
||||
if (existing?.root_hash !== root) {
|
||||
await this.fetchMembersFromGroup(root, provider, name);
|
||||
await this.interepGroups?.addHash(root, provider, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
this.timeout = setTimeout(this.sync, INTEREP_SYNC_INTERVAL);
|
||||
}
|
||||
};
|
||||
async getBatchFromRootHash(rootHash: string) {
|
||||
try {
|
||||
const interepGroups = await this.call('db', 'getInterepGroups');
|
||||
|
||||
async fetchMembersFromGroup(
|
||||
root: string,
|
||||
provider: string,
|
||||
name: string,
|
||||
limit = 1000,
|
||||
offset = 0
|
||||
): Promise<void> {
|
||||
const groupId = `interrep_${provider}_${name}`;
|
||||
// @ts-ignore
|
||||
const resp = await fetch(
|
||||
`${config.interrepAPI}/api/v1/groups/${provider}/${name}/members?limit=${limit}&offset=${offset}`
|
||||
);
|
||||
const json = await resp.json();
|
||||
const members: string[] = json.data;
|
||||
if (members.length) {
|
||||
for (const member of members) {
|
||||
await this.semaphore?.addID(BigInt(member).toString(16), groupId, root);
|
||||
}
|
||||
}
|
||||
const exist = await interepGroups.findOneByHash(rootHash);
|
||||
|
||||
if (members.length >= limit) {
|
||||
await this.fetchMembersFromGroup(root, provider, name, limit + 1000, limit);
|
||||
}
|
||||
if (exist) return exist;
|
||||
|
||||
// @ts-ignore
|
||||
const resp = await fetch(`${config.interrepAPI}/api/v1/batches/${rootHash}`);
|
||||
const json = await resp.json();
|
||||
const group = json?.data?.group;
|
||||
|
||||
if (group) {
|
||||
await interepGroups.addHash(rootHash, group.provider, group.name);
|
||||
}
|
||||
|
||||
return {
|
||||
name: group.name,
|
||||
provider: group.provider,
|
||||
root_hash: rootHash,
|
||||
};
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async getProofFromGroup(provider: string, name: string, id: string) {
|
||||
try {
|
||||
// @ts-ignore
|
||||
const resp = await fetch(
|
||||
`${config.interrepAPI}/api/v1/groups/${provider}/${name}/${id}/proof`
|
||||
);
|
||||
const json = await resp.json();
|
||||
return json;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async inProvider(provider: string, id: string): Promise<boolean> {
|
||||
// @ts-ignore
|
||||
const resp = await fetch(`${config.interrepAPI}/api/v1/providers/${provider}/${id}`);
|
||||
const json = await resp.json();
|
||||
|
||||
if (json?.data) {
|
||||
return !!json?.data;
|
||||
}
|
||||
|
||||
async getBatchFromRootHash(rootHash: string) {
|
||||
try {
|
||||
const interepGroups = await this.call('db', 'getInterepGroups');
|
||||
return false;
|
||||
}
|
||||
|
||||
const exist = await interepGroups.findOneByHash(rootHash);
|
||||
async start() {
|
||||
this.interepGroups = await interepGroups(sequelize);
|
||||
this.semaphore = await semaphore(sequelize);
|
||||
await this.sync();
|
||||
}
|
||||
|
||||
if (exist) return exist;
|
||||
|
||||
// @ts-ignore
|
||||
const resp = await fetch(`${config.interrepAPI}/api/v1/batches/${rootHash}`);
|
||||
const json = await resp.json();
|
||||
const group = json?.data?.group;
|
||||
|
||||
if (group) {
|
||||
await interepGroups.addHash(rootHash, group.provider, group.name);
|
||||
}
|
||||
|
||||
return {
|
||||
name: group.name,
|
||||
provider: group.provider,
|
||||
root_hash: rootHash,
|
||||
};
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async getProofFromGroup(provider: string, name: string, id: string) {
|
||||
try {
|
||||
// @ts-ignore
|
||||
const resp = await fetch(
|
||||
`${config.interrepAPI}/api/v1/groups/${provider}/${name}/${id}/proof`
|
||||
);
|
||||
const json = await resp.json();
|
||||
return json;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async inProvider(provider: string, id: string): Promise<boolean> {
|
||||
// @ts-ignore
|
||||
const resp = await fetch(`${config.interrepAPI}/api/v1/providers/${provider}/${id}`);
|
||||
const json = await resp.json();
|
||||
|
||||
if (json?.data) {
|
||||
return !!json?.data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async start() {
|
||||
this.interepGroups = await interepGroups(sequelize);
|
||||
this.semaphore = await semaphore(sequelize);
|
||||
await this.sync();
|
||||
}
|
||||
|
||||
async stop() {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
}
|
||||
async stop() {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,21 +3,21 @@ import config from '../util/config';
|
||||
import { CIDString, Filelike, Web3Storage } from 'web3.storage';
|
||||
|
||||
export default class IPFSService extends GenericService {
|
||||
client: Web3Storage;
|
||||
client: Web3Storage;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// @ts-ignore
|
||||
this.client = new Web3Storage({
|
||||
token: config.web3StorageAPIKey as string,
|
||||
});
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
// @ts-ignore
|
||||
this.client = new Web3Storage({
|
||||
token: config.web3StorageAPIKey as string,
|
||||
});
|
||||
}
|
||||
|
||||
store = (files: Iterable<Filelike>): Promise<CIDString> => {
|
||||
return this.client.put(files);
|
||||
};
|
||||
store = (files: Iterable<Filelike>): Promise<CIDString> => {
|
||||
return this.client.put(files);
|
||||
};
|
||||
|
||||
status = (cid: CIDString) => {
|
||||
return this.client.status(cid);
|
||||
};
|
||||
status = (cid: CIDString) => {
|
||||
return this.client.status(cid);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,170 +7,170 @@ import { sequelize } from '../util/sequelize';
|
||||
import semaphore from '../models/semaphore';
|
||||
|
||||
export default class MerkleService extends GenericService {
|
||||
merkleRoot: ReturnType<typeof merkleRoot>;
|
||||
semaphore: ReturnType<typeof semaphore>;
|
||||
merkleRoot: ReturnType<typeof merkleRoot>;
|
||||
semaphore: ReturnType<typeof semaphore>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.merkleRoot = merkleRoot(sequelize);
|
||||
this.semaphore = semaphore(sequelize);
|
||||
constructor() {
|
||||
super();
|
||||
this.merkleRoot = merkleRoot(sequelize);
|
||||
this.semaphore = semaphore(sequelize);
|
||||
}
|
||||
|
||||
getAllLeaves = async (group: string): Promise<any[]> => {
|
||||
const [protocol, groupName, groupType = ''] = group.split('_');
|
||||
const protocolBucket = SQL[protocol] || {};
|
||||
const groupBucket = protocolBucket[groupName] || {};
|
||||
const { sql, replacement } = groupBucket[groupType] || {};
|
||||
let query = '';
|
||||
const options: QueryOptions = { type: QueryTypes.SELECT };
|
||||
|
||||
if (protocol === 'custom') {
|
||||
query = customGroupSQL;
|
||||
options.replacements = {
|
||||
group_address: groupName,
|
||||
};
|
||||
} else {
|
||||
query = sql;
|
||||
options.replacements = replacement || {
|
||||
group_id: group,
|
||||
};
|
||||
}
|
||||
|
||||
getAllLeaves = async (group: string): Promise<any[]> => {
|
||||
const [protocol, groupName, groupType = ''] = group.split('_');
|
||||
const protocolBucket = SQL[protocol] || {};
|
||||
const groupBucket = protocolBucket[groupName] || {};
|
||||
const { sql, replacement } = groupBucket[groupType] || {};
|
||||
let query = '';
|
||||
const options: QueryOptions = { type: QueryTypes.SELECT };
|
||||
if (!query) throw new Error(`${group} does not exist`);
|
||||
|
||||
if (protocol === 'custom') {
|
||||
query = customGroupSQL;
|
||||
options.replacements = {
|
||||
group_address: groupName,
|
||||
};
|
||||
} else {
|
||||
query = sql;
|
||||
options.replacements = replacement || {
|
||||
group_id: group,
|
||||
};
|
||||
}
|
||||
const leaves = await sequelize.query(query, options);
|
||||
return leaves;
|
||||
};
|
||||
|
||||
if (!query) throw new Error(`${group} does not exist`);
|
||||
makeTree = async (
|
||||
group: string,
|
||||
zkType: 'rln' | 'semaphore' = 'rln'
|
||||
): Promise<IncrementalMerkleTree> => {
|
||||
const [protocol, groupName, groupType = ''] = group.split('_');
|
||||
const protocolBucket = SQL[protocol] || {};
|
||||
const groupBucket = protocolBucket[groupName] || {};
|
||||
const { sql, replacement } = groupBucket[groupType] || {};
|
||||
let query = '';
|
||||
const options: QueryOptions = { type: QueryTypes.SELECT };
|
||||
|
||||
const leaves = await sequelize.query(query, options);
|
||||
return leaves;
|
||||
if (protocol === 'custom') {
|
||||
query = customGroupSQL;
|
||||
options.replacements = {
|
||||
group_address: groupName,
|
||||
};
|
||||
} else {
|
||||
query = sql;
|
||||
options.replacements = replacement || {
|
||||
group_id: group,
|
||||
};
|
||||
}
|
||||
|
||||
if (!query) throw new Error(`${group} does not exist`);
|
||||
|
||||
const leaves = await sequelize.query(query, options);
|
||||
const tree = generateMerkleTree(
|
||||
zkType === 'rln' ? 15 : 20,
|
||||
BigInt(0),
|
||||
leaves.map(({ id_commitment }: any) => '0x' + id_commitment)
|
||||
);
|
||||
|
||||
return tree;
|
||||
};
|
||||
|
||||
getGroupByRoot = async (root: string): Promise<string | null> => {
|
||||
const exist = await this.merkleRoot.getGroupByRoot(root);
|
||||
return exist?.group_id || null;
|
||||
};
|
||||
|
||||
verifyProof = async (proof: MerkleProof): Promise<string | null> => {
|
||||
const groups = [
|
||||
'zksocial_all',
|
||||
'interrep_twitter_unrated',
|
||||
'interrep_twitter_bronze',
|
||||
'interrep_twitter_silver',
|
||||
'interrep_twitter_gold',
|
||||
];
|
||||
|
||||
const existingGroup = await this.getGroupByRoot(proof.root);
|
||||
|
||||
if (existingGroup) {
|
||||
const tree = await this.makeTree(existingGroup);
|
||||
if (tree.verifyProof(proof)) return existingGroup;
|
||||
}
|
||||
|
||||
for (const group of groups) {
|
||||
const tree = await this.makeTree(group);
|
||||
if (tree.verifyProof(proof)) return group;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
findProof = async (idCommitment: string, group?: string, _proofType?: 'semaphore' | 'rln') => {
|
||||
if (!group) {
|
||||
const row = await this.semaphore.findOneByCommitment(idCommitment);
|
||||
if (!row) throw new Error(`${idCommitment} is not in any groups`);
|
||||
group = row.group_id;
|
||||
}
|
||||
|
||||
const exist = await this.semaphore.findOne(idCommitment, group);
|
||||
const [protocol, service] = group.split('_');
|
||||
|
||||
if (!exist && protocol === 'interrep') {
|
||||
await this.call('interrep', 'syncOne', group).catch(() => null);
|
||||
}
|
||||
|
||||
const proofType = _proofType ? _proofType : service === 'taz' ? 'semaphore' : 'rln';
|
||||
const tree = await this.makeTree(group, proofType);
|
||||
const proof = await tree.createProof(tree.indexOf(BigInt('0x' + idCommitment)));
|
||||
|
||||
if (!proof) {
|
||||
throw new Error(`${idCommitment} is not in ${group}`);
|
||||
}
|
||||
|
||||
const root = '0x' + proof.root.toString(16);
|
||||
|
||||
await this.addRoot(root, group);
|
||||
|
||||
const retProof = {
|
||||
root,
|
||||
siblings: proof.siblings.map(siblings =>
|
||||
Array.isArray(siblings)
|
||||
? siblings.map(element => '0x' + element.toString(16))
|
||||
: '0x' + siblings.toString(16)
|
||||
),
|
||||
pathIndices: proof.pathIndices,
|
||||
leaf: '0x' + proof.leaf.toString(16),
|
||||
group: group,
|
||||
};
|
||||
|
||||
makeTree = async (
|
||||
group: string,
|
||||
zkType: 'rln' | 'semaphore' = 'rln'
|
||||
): Promise<IncrementalMerkleTree> => {
|
||||
const [protocol, groupName, groupType = ''] = group.split('_');
|
||||
const protocolBucket = SQL[protocol] || {};
|
||||
const groupBucket = protocolBucket[groupName] || {};
|
||||
const { sql, replacement } = groupBucket[groupType] || {};
|
||||
let query = '';
|
||||
const options: QueryOptions = { type: QueryTypes.SELECT };
|
||||
return retProof;
|
||||
};
|
||||
|
||||
if (protocol === 'custom') {
|
||||
query = customGroupSQL;
|
||||
options.replacements = {
|
||||
group_address: groupName,
|
||||
};
|
||||
} else {
|
||||
query = sql;
|
||||
options.replacements = replacement || {
|
||||
group_id: group,
|
||||
};
|
||||
}
|
||||
addRoot = async (rootHash: string, group: string) => {
|
||||
return this.merkleRoot.addRoot(rootHash, group);
|
||||
};
|
||||
|
||||
if (!query) throw new Error(`${group} does not exist`);
|
||||
|
||||
const leaves = await sequelize.query(query, options);
|
||||
const tree = generateMerkleTree(
|
||||
zkType === 'rln' ? 15 : 20,
|
||||
BigInt(0),
|
||||
leaves.map(({ id_commitment }: any) => '0x' + id_commitment)
|
||||
);
|
||||
|
||||
return tree;
|
||||
};
|
||||
|
||||
getGroupByRoot = async (root: string): Promise<string | null> => {
|
||||
const exist = await this.merkleRoot.getGroupByRoot(root);
|
||||
return exist?.group_id || null;
|
||||
};
|
||||
|
||||
verifyProof = async (proof: MerkleProof): Promise<string | null> => {
|
||||
const groups = [
|
||||
'zksocial_all',
|
||||
'interrep_twitter_unrated',
|
||||
'interrep_twitter_bronze',
|
||||
'interrep_twitter_silver',
|
||||
'interrep_twitter_gold',
|
||||
];
|
||||
|
||||
const existingGroup = await this.getGroupByRoot(proof.root);
|
||||
|
||||
if (existingGroup) {
|
||||
const tree = await this.makeTree(existingGroup);
|
||||
if (tree.verifyProof(proof)) return existingGroup;
|
||||
}
|
||||
|
||||
for (const group of groups) {
|
||||
const tree = await this.makeTree(group);
|
||||
if (tree.verifyProof(proof)) return group;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
findProof = async (idCommitment: string, group?: string, _proofType?: 'semaphore' | 'rln') => {
|
||||
if (!group) {
|
||||
const row = await this.semaphore.findOneByCommitment(idCommitment);
|
||||
if (!row) throw new Error(`${idCommitment} is not in any groups`);
|
||||
group = row.group_id;
|
||||
}
|
||||
|
||||
const exist = await this.semaphore.findOne(idCommitment, group);
|
||||
const [protocol, service] = group.split('_');
|
||||
|
||||
if (!exist && protocol === 'interrep') {
|
||||
await this.call('interrep', 'syncOne', group).catch(() => null);
|
||||
}
|
||||
|
||||
const proofType = _proofType ? _proofType : service === 'taz' ? 'semaphore' : 'rln';
|
||||
const tree = await this.makeTree(group, proofType);
|
||||
const proof = await tree.createProof(tree.indexOf(BigInt('0x' + idCommitment)));
|
||||
|
||||
if (!proof) {
|
||||
throw new Error(`${idCommitment} is not in ${group}`);
|
||||
}
|
||||
|
||||
const root = '0x' + proof.root.toString(16);
|
||||
|
||||
await this.addRoot(root, group);
|
||||
|
||||
const retProof = {
|
||||
root,
|
||||
siblings: proof.siblings.map(siblings =>
|
||||
Array.isArray(siblings)
|
||||
? siblings.map(element => '0x' + element.toString(16))
|
||||
: '0x' + siblings.toString(16)
|
||||
),
|
||||
pathIndices: proof.pathIndices,
|
||||
leaf: '0x' + proof.leaf.toString(16),
|
||||
group: group,
|
||||
};
|
||||
|
||||
return retProof;
|
||||
};
|
||||
|
||||
addRoot = async (rootHash: string, group: string) => {
|
||||
return this.merkleRoot.addRoot(rootHash, group);
|
||||
};
|
||||
|
||||
findRoot = async (rootHash: string) => {
|
||||
const cached = await this.merkleRoot.getGroupByRoot(rootHash);
|
||||
return cached?.group_id;
|
||||
};
|
||||
findRoot = async (rootHash: string) => {
|
||||
const cached = await this.merkleRoot.getGroupByRoot(rootHash);
|
||||
return cached?.group_id;
|
||||
};
|
||||
}
|
||||
|
||||
const SQL: {
|
||||
[protocol: string]: {
|
||||
[groupName: string]: {
|
||||
[groupType: string]: {
|
||||
sql: string;
|
||||
replacement?: BindOrReplacements;
|
||||
};
|
||||
};
|
||||
[protocol: string]: {
|
||||
[groupName: string]: {
|
||||
[groupType: string]: {
|
||||
sql: string;
|
||||
replacement?: BindOrReplacements;
|
||||
};
|
||||
};
|
||||
};
|
||||
} = {
|
||||
zksocial: {
|
||||
all: {
|
||||
'': {
|
||||
sql: `
|
||||
zksocial: {
|
||||
all: {
|
||||
'': {
|
||||
sql: `
|
||||
SELECT u.name as address, pf.value as id_commitment FROM users u
|
||||
LEFT JOIN profiles pf ON pf."messageId" = (
|
||||
SELECT "messageId" FROM profiles
|
||||
@@ -180,34 +180,34 @@ const SQL: {
|
||||
)
|
||||
WHERE pf.value IS NOT NULL
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
interrep: {
|
||||
twitter: {
|
||||
unrated: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
bronze: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
silver: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
gold: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
},
|
||||
github: {
|
||||
unrated: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
bronze: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
silver: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
gold: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
},
|
||||
reddit: {
|
||||
unrated: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
bronze: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
silver: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
gold: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
},
|
||||
},
|
||||
interrep: {
|
||||
twitter: {
|
||||
unrated: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
bronze: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
silver: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
gold: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
},
|
||||
semaphore: {
|
||||
taz: {
|
||||
members: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
},
|
||||
github: {
|
||||
unrated: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
bronze: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
silver: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
gold: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
},
|
||||
reddit: {
|
||||
unrated: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
bronze: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
silver: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
gold: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
},
|
||||
},
|
||||
semaphore: {
|
||||
taz: {
|
||||
members: { sql: `SELECT id_commitment FROM semaphores WHERE group_id = :group_id` },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const customGroupSQL = `
|
||||
|
||||
@@ -8,57 +8,53 @@ import { semaphoreABI } from '../../util/abi';
|
||||
import { Contract } from 'web3-eth-contract';
|
||||
|
||||
export default class Group42 extends GenericService {
|
||||
semaphore?: ReturnType<typeof semaphore>;
|
||||
app: ReturnType<typeof app>;
|
||||
web3: Web3;
|
||||
contract: Contract;
|
||||
semaphore?: ReturnType<typeof semaphore>;
|
||||
app: ReturnType<typeof app>;
|
||||
web3: Web3;
|
||||
contract: Contract;
|
||||
|
||||
constructor(opts: { app: ReturnType<typeof app> }) {
|
||||
super();
|
||||
const httpProvider = new Web3.providers.HttpProvider(config.goerliHttpProvider);
|
||||
this.web3 = new Web3(httpProvider);
|
||||
this.contract = new this.web3.eth.Contract(
|
||||
semaphoreABI as any,
|
||||
'0xE585f0Db9aB24dC912404DFfb9b28fb8BF211fA6'
|
||||
);
|
||||
this.app = opts.app;
|
||||
constructor(opts: { app: ReturnType<typeof app> }) {
|
||||
super();
|
||||
const httpProvider = new Web3.providers.HttpProvider(config.goerliHttpProvider);
|
||||
this.web3 = new Web3(httpProvider);
|
||||
this.contract = new this.web3.eth.Contract(
|
||||
semaphoreABI as any,
|
||||
'0xE585f0Db9aB24dC912404DFfb9b28fb8BF211fA6'
|
||||
);
|
||||
this.app = opts.app;
|
||||
}
|
||||
|
||||
async start() {
|
||||
this.semaphore = await semaphore(sequelize);
|
||||
}
|
||||
|
||||
async sync() {
|
||||
const data = await this.app.read();
|
||||
const lastBlock = data?.lastGroup42BlockScanned;
|
||||
const block = await this.web3.eth.getBlock('latest');
|
||||
const toBlock = Math.min(block.number, lastBlock + 99999);
|
||||
|
||||
const events = await this.contract.getPastEvents('MemberAdded', {
|
||||
fromBlock: lastBlock,
|
||||
toBlock: toBlock,
|
||||
});
|
||||
|
||||
for (const event of events) {
|
||||
const identityCommitment = event.returnValues.identityCommitment;
|
||||
const groupId = event.returnValues.groupId;
|
||||
const merkleTreeRoot = event.returnValues.merkleTreeRoot;
|
||||
const idCommitmentHex = BigInt(identityCommitment).toString(16);
|
||||
const exist = await this.semaphore?.findOne(idCommitmentHex, 'semaphore_taz_members');
|
||||
|
||||
if (!exist && (groupId === '10806' || groupId === '42' || groupId === '10807')) {
|
||||
await this.semaphore?.addID(idCommitmentHex, 'semaphore_taz_members', merkleTreeRoot);
|
||||
}
|
||||
}
|
||||
|
||||
async start() {
|
||||
this.semaphore = await semaphore(sequelize);
|
||||
}
|
||||
|
||||
async sync() {
|
||||
const data = await this.app.read();
|
||||
const lastBlock = data?.lastGroup42BlockScanned;
|
||||
const block = await this.web3.eth.getBlock('latest');
|
||||
const toBlock = Math.min(block.number, lastBlock + 99999);
|
||||
|
||||
const events = await this.contract.getPastEvents('MemberAdded', {
|
||||
fromBlock: lastBlock,
|
||||
toBlock: toBlock,
|
||||
});
|
||||
|
||||
for (const event of events) {
|
||||
const identityCommitment = event.returnValues.identityCommitment;
|
||||
const groupId = event.returnValues.groupId;
|
||||
const merkleTreeRoot = event.returnValues.merkleTreeRoot;
|
||||
const idCommitmentHex = BigInt(identityCommitment).toString(16);
|
||||
const exist = await this.semaphore?.findOne(idCommitmentHex, 'semaphore_taz_members');
|
||||
|
||||
if (!exist && (groupId === '10806' || groupId === '42' || groupId === '10807')) {
|
||||
await this.semaphore?.addID(
|
||||
idCommitmentHex,
|
||||
'semaphore_taz_members',
|
||||
merkleTreeRoot
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await this.app!.updateLastGroup42BlockScanned(toBlock);
|
||||
|
||||
if (block.number > toBlock) {
|
||||
await this.sync();
|
||||
}
|
||||
await this.app!.updateLastGroup42BlockScanned(toBlock);
|
||||
|
||||
if (block.number > toBlock) {
|
||||
await this.sync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,23 @@ import { GenericService } from '../../util/svc';
|
||||
import Group42 from './group42';
|
||||
|
||||
export class ReputationService extends GenericService {
|
||||
group42?: Group42;
|
||||
group42?: Group42;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async start() {
|
||||
const app = await this.call('db', 'getApp');
|
||||
this.group42 = new Group42({
|
||||
app,
|
||||
});
|
||||
await this.group42.start();
|
||||
this.sync();
|
||||
}
|
||||
async start() {
|
||||
const app = await this.call('db', 'getApp');
|
||||
this.group42 = new Group42({
|
||||
app,
|
||||
});
|
||||
await this.group42.start();
|
||||
this.sync();
|
||||
}
|
||||
|
||||
sync = async () => {
|
||||
await this.group42!.sync();
|
||||
setTimeout(this.sync, 30000);
|
||||
};
|
||||
sync = async () => {
|
||||
await this.group42!.sync();
|
||||
setTimeout(this.sync, 30000);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,87 +6,87 @@ import config from '../../lib/zk-chat-server/src/utils/config';
|
||||
import { RLN, RLNFullProof, SemaphoreFullProof } from '@zk-kit/protocols';
|
||||
|
||||
export default class ZKChatService extends GenericService {
|
||||
zkchat: ZKChat;
|
||||
sequelize: Sequelize;
|
||||
zkchat: ZKChat;
|
||||
sequelize: Sequelize;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.zkchat = new ZKChat();
|
||||
this.sequelize = new Sequelize(
|
||||
config.DB_NAME as string,
|
||||
config.DB_USERNAME as string,
|
||||
config.DB_PASSWORD,
|
||||
{
|
||||
host: config.DB_HOST,
|
||||
port: Number(config.DB_PORT),
|
||||
dialect: config.DB_DIALECT as Dialect,
|
||||
logging: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
this.zkchat = new ZKChat();
|
||||
this.sequelize = new Sequelize(
|
||||
config.DB_NAME as string,
|
||||
config.DB_USERNAME as string,
|
||||
config.DB_PASSWORD,
|
||||
{
|
||||
host: config.DB_HOST,
|
||||
port: Number(config.DB_PORT),
|
||||
dialect: config.DB_DIALECT as Dialect,
|
||||
logging: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
start = async () => {
|
||||
return this.zkchat.init();
|
||||
};
|
||||
start = async () => {
|
||||
return this.zkchat.init();
|
||||
};
|
||||
|
||||
registerUser = async (address: string, ecdhPubkey: string) => {
|
||||
return this.zkchat.registerUser(address, ecdhPubkey);
|
||||
};
|
||||
registerUser = async (address: string, ecdhPubkey: string) => {
|
||||
return this.zkchat.registerUser(address, ecdhPubkey);
|
||||
};
|
||||
|
||||
getAllUsers = async (offset = 0, limit = 20) => {
|
||||
return this.zkchat.getAllUsers(offset, limit);
|
||||
};
|
||||
getAllUsers = async (offset = 0, limit = 20) => {
|
||||
return this.zkchat.getAllUsers(offset, limit);
|
||||
};
|
||||
|
||||
addChatMessage = async (chatMessage: ChatMessage) => {
|
||||
return this.zkchat.addChatMessage(chatMessage);
|
||||
};
|
||||
addChatMessage = async (chatMessage: ChatMessage) => {
|
||||
return this.zkchat.addChatMessage(chatMessage);
|
||||
};
|
||||
|
||||
getDirectMessages = async (
|
||||
senderPubkey: string,
|
||||
receiverPubkey: string,
|
||||
offset = 0,
|
||||
limit = 20
|
||||
) => {
|
||||
return this.zkchat.getDirectMessages(senderPubkey, receiverPubkey, offset, limit);
|
||||
};
|
||||
getDirectMessages = async (
|
||||
senderPubkey: string,
|
||||
receiverPubkey: string,
|
||||
offset = 0,
|
||||
limit = 20
|
||||
) => {
|
||||
return this.zkchat.getDirectMessages(senderPubkey, receiverPubkey, offset, limit);
|
||||
};
|
||||
|
||||
getDirectChatsForUser = async (pubkey: string) => {
|
||||
return this.zkchat.getDirectChatsForUser(pubkey);
|
||||
};
|
||||
getDirectChatsForUser = async (pubkey: string) => {
|
||||
return this.zkchat.getDirectChatsForUser(pubkey);
|
||||
};
|
||||
|
||||
isEpochCurrent = async (epoch: string) => {
|
||||
return this.zkchat.isEpochCurrent(epoch);
|
||||
};
|
||||
isEpochCurrent = async (epoch: string) => {
|
||||
return this.zkchat.isEpochCurrent(epoch);
|
||||
};
|
||||
|
||||
verifyRLNProof = async (proof: RLNFullProof) => {
|
||||
return this.zkchat.verifyRLNProof(proof);
|
||||
};
|
||||
verifyRLNProof = async (proof: RLNFullProof) => {
|
||||
return this.zkchat.verifyRLNProof(proof);
|
||||
};
|
||||
|
||||
verifySemaphoreProof = async (proof: SemaphoreFullProof) => {
|
||||
return this.zkchat.verifySemaphoreProof(proof);
|
||||
};
|
||||
verifySemaphoreProof = async (proof: SemaphoreFullProof) => {
|
||||
return this.zkchat.verifySemaphoreProof(proof);
|
||||
};
|
||||
|
||||
checkShare = async (share: {
|
||||
nullifier: string;
|
||||
epoch: string;
|
||||
x_share: string;
|
||||
y_share: string;
|
||||
}) => {
|
||||
return this.zkchat.checkShare(share);
|
||||
};
|
||||
checkShare = async (share: {
|
||||
nullifier: string;
|
||||
epoch: string;
|
||||
x_share: string;
|
||||
y_share: string;
|
||||
}) => {
|
||||
return this.zkchat.checkShare(share);
|
||||
};
|
||||
|
||||
insertShare = async (share: {
|
||||
nullifier: string;
|
||||
epoch: string;
|
||||
x_share: string;
|
||||
y_share: string;
|
||||
}) => {
|
||||
return this.zkchat.insertShare(share);
|
||||
};
|
||||
insertShare = async (share: {
|
||||
nullifier: string;
|
||||
epoch: string;
|
||||
x_share: string;
|
||||
y_share: string;
|
||||
}) => {
|
||||
return this.zkchat.insertShare(share);
|
||||
};
|
||||
|
||||
searchChats = async (query: string, sender?: string, offset = 0, limit = 20) => {
|
||||
const values = await this.sequelize.query(
|
||||
`
|
||||
searchChats = async (query: string, sender?: string, offset = 0, limit = 20) => {
|
||||
const values = await this.sequelize.query(
|
||||
`
|
||||
SELECT
|
||||
ecdh.value as receiver_ecdh,
|
||||
idcommitment.value as receiver_idcommitment,
|
||||
@@ -103,9 +103,9 @@ export default class ZKChatService extends GenericService {
|
||||
OR LOWER(name.creator) IN (SELECT LOWER(creator) from profiles WHERE subtype = 'NAME' AND LOWER(value) LIKE :query ORDER BY "createdAt" DESC LIMIT 1)
|
||||
)
|
||||
${
|
||||
!sender
|
||||
? ''
|
||||
: `
|
||||
!sender
|
||||
? ''
|
||||
: `
|
||||
AND (
|
||||
zku.wallet_address IN (SELECT distinct zk.receiver_address FROM zkchat_chats zk WHERE zk.sender_address = :sender)
|
||||
OR zku.wallet_address IN (SELECT distinct zk.sender_address FROM zkchat_chats zk WHERE zk.receiver_address = :sender)
|
||||
@@ -114,17 +114,17 @@ export default class ZKChatService extends GenericService {
|
||||
|
||||
LIMIT :limit OFFSET :offset
|
||||
`,
|
||||
{
|
||||
type: QueryTypes.SELECT,
|
||||
replacements: {
|
||||
query: `%${query.toLowerCase()}%`,
|
||||
sender,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
}
|
||||
);
|
||||
{
|
||||
type: QueryTypes.SELECT,
|
||||
replacements: {
|
||||
query: `%${query.toLowerCase()}%`,
|
||||
sender,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return values;
|
||||
};
|
||||
return values;
|
||||
};
|
||||
}
|
||||
|
||||
2252
src/util/abi.ts
2252
src/util/abi.ts
File diff suppressed because it is too large
Load Diff
@@ -2,46 +2,46 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
let json: {
|
||||
interrepAPI?: string;
|
||||
web3HttpProvider?: string;
|
||||
arbitrumHttpProvider?: string;
|
||||
goerliHttpProvider?: string;
|
||||
arbitrumRegistrar?: string;
|
||||
arbitrumPrivateKey?: string;
|
||||
arbitrumAddress?: string;
|
||||
ensResolver?: string;
|
||||
interrepContract?: string;
|
||||
dbDialect?: string;
|
||||
dbStorage?: string;
|
||||
dbName?: string;
|
||||
dbUsername?: string;
|
||||
dbPassword?: string;
|
||||
dbHost?: string;
|
||||
dbPort?: string;
|
||||
port?: number;
|
||||
gunPort?: number;
|
||||
gunPeers?: string[];
|
||||
moderators?: string[];
|
||||
jwtSecret?: string;
|
||||
twCallbackUrl?: string;
|
||||
twConsumerKey?: string;
|
||||
twConsumerSecret?: string;
|
||||
twBearerToken?: string;
|
||||
twAccessKey?: string;
|
||||
twAccessSecret?: string;
|
||||
rapidAPIKey?: string;
|
||||
web3StorageAPIKey?: string;
|
||||
interrepAPI?: string;
|
||||
web3HttpProvider?: string;
|
||||
arbitrumHttpProvider?: string;
|
||||
goerliHttpProvider?: string;
|
||||
arbitrumRegistrar?: string;
|
||||
arbitrumPrivateKey?: string;
|
||||
arbitrumAddress?: string;
|
||||
ensResolver?: string;
|
||||
interrepContract?: string;
|
||||
dbDialect?: string;
|
||||
dbStorage?: string;
|
||||
dbName?: string;
|
||||
dbUsername?: string;
|
||||
dbPassword?: string;
|
||||
dbHost?: string;
|
||||
dbPort?: string;
|
||||
port?: number;
|
||||
gunPort?: number;
|
||||
gunPeers?: string[];
|
||||
moderators?: string[];
|
||||
jwtSecret?: string;
|
||||
twCallbackUrl?: string;
|
||||
twConsumerKey?: string;
|
||||
twConsumerSecret?: string;
|
||||
twBearerToken?: string;
|
||||
twAccessKey?: string;
|
||||
twAccessSecret?: string;
|
||||
rapidAPIKey?: string;
|
||||
web3StorageAPIKey?: string;
|
||||
} = {};
|
||||
|
||||
try {
|
||||
const configBuffer =
|
||||
process.env.NODE_ENV === 'production'
|
||||
? fs.readFileSync(path.join(process.cwd(), 'config.prod.json'))
|
||||
: process.env.NODE_ENV === 'test'
|
||||
? fs.readFileSync(path.join(process.cwd(), 'config.test.json'))
|
||||
: fs.readFileSync(path.join(process.cwd(), 'config.dev.json'));
|
||||
const parsed = JSON.parse(configBuffer.toString('utf-8'));
|
||||
json = parsed;
|
||||
const configBuffer =
|
||||
process.env.NODE_ENV === 'production'
|
||||
? fs.readFileSync(path.join(process.cwd(), 'config.prod.json'))
|
||||
: process.env.NODE_ENV === 'test'
|
||||
? fs.readFileSync(path.join(process.cwd(), 'config.test.json'))
|
||||
: fs.readFileSync(path.join(process.cwd(), 'config.dev.json'));
|
||||
const parsed = JSON.parse(configBuffer.toString('utf-8'));
|
||||
json = parsed;
|
||||
} catch (e) {}
|
||||
|
||||
const rapidAPIKey = json.rapidAPIKey || process.env.RAPIDAPI_KEY;
|
||||
@@ -69,10 +69,10 @@ const port = json.port || process.env.PORT;
|
||||
const gunPort = json.gunPort || process.env.GUN_PORT;
|
||||
const gunPeers = json.gunPeers || process.env?.GUN_PEERS?.split(' ') || [];
|
||||
const moderators = json.moderators ||
|
||||
process.env?.MODERATORS?.split(' ') || [
|
||||
'0x3F425586D68616A113C29c303766DAD444167EE8',
|
||||
'0xd44a82dD160217d46D754a03C8f841edF06EBE3c',
|
||||
];
|
||||
process.env?.MODERATORS?.split(' ') || [
|
||||
'0x3F425586D68616A113C29c303766DAD444167EE8',
|
||||
'0xd44a82dD160217d46D754a03C8f841edF06EBE3c',
|
||||
];
|
||||
const interrepAPI = json.interrepAPI || process.env.INTERREP_API || 'https://kovan.interep.link';
|
||||
const interrepContract = json.interrepContract || process.env.INTERREP_CONTRACT || '';
|
||||
const jwtSecret = json.jwtSecret || process.env.JWT_SECRET || 'topjwtsecret';
|
||||
@@ -96,35 +96,35 @@ if (!rapidAPIKey) throw new Error(`rapidAPIKey is not valid`);
|
||||
if (!web3StorageAPIKey) throw new Error(`web3StorageAPIKey is not valid`);
|
||||
|
||||
const config = {
|
||||
interrepAPI,
|
||||
web3HttpProvider,
|
||||
arbitrumHttpProvider,
|
||||
goerliHttpProvider,
|
||||
arbitrumRegistrar,
|
||||
arbitrumPrivateKey,
|
||||
arbitrumAddress,
|
||||
ensResolver,
|
||||
interrepContract,
|
||||
dbDialect,
|
||||
dbStorage,
|
||||
dbName,
|
||||
dbUsername,
|
||||
dbPassword,
|
||||
dbHost,
|
||||
dbPort,
|
||||
port: port ? Number(port) : 3000,
|
||||
gunPort: gunPort ? Number(gunPort) : 8765,
|
||||
gunPeers,
|
||||
jwtSecret,
|
||||
twCallbackUrl,
|
||||
twConsumerKey,
|
||||
twConsumerSecret,
|
||||
twBearerToken,
|
||||
twAccessKey,
|
||||
twAccessSecret,
|
||||
rapidAPIKey,
|
||||
moderators,
|
||||
web3StorageAPIKey,
|
||||
interrepAPI,
|
||||
web3HttpProvider,
|
||||
arbitrumHttpProvider,
|
||||
goerliHttpProvider,
|
||||
arbitrumRegistrar,
|
||||
arbitrumPrivateKey,
|
||||
arbitrumAddress,
|
||||
ensResolver,
|
||||
interrepContract,
|
||||
dbDialect,
|
||||
dbStorage,
|
||||
dbName,
|
||||
dbUsername,
|
||||
dbPassword,
|
||||
dbHost,
|
||||
dbPort,
|
||||
port: port ? Number(port) : 3000,
|
||||
gunPort: gunPort ? Number(gunPort) : 8765,
|
||||
gunPeers,
|
||||
jwtSecret,
|
||||
twCallbackUrl,
|
||||
twConsumerKey,
|
||||
twConsumerSecret,
|
||||
twBearerToken,
|
||||
twAccessKey,
|
||||
twAccessSecret,
|
||||
rapidAPIKey,
|
||||
moderators,
|
||||
web3StorageAPIKey,
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -4,15 +4,15 @@ import tape from 'tape';
|
||||
import { verifySignatureP256 } from './crypto';
|
||||
|
||||
tape('crypto.ts', async t => {
|
||||
const signature =
|
||||
'304402205eef94ed090d04f273b756a206dcabe875dc22e3be863d9d45065c2e405b3bdc022042f676412c5f0579c256f2ee7744950e3a661695e270c7bbaddbff9be2ac8357.0xf622d6eC8a21532a62BA2CAFdda571c24D670E5c';
|
||||
const [sig, address] = signature.split('.');
|
||||
const signature =
|
||||
'304402205eef94ed090d04f273b756a206dcabe875dc22e3be863d9d45065c2e405b3bdc022042f676412c5f0579c256f2ee7744950e3a661695e270c7bbaddbff9be2ac8357.0xf622d6eC8a21532a62BA2CAFdda571c24D670E5c';
|
||||
const [sig, address] = signature.split('.');
|
||||
|
||||
const verified = verifySignatureP256(
|
||||
'cl-5YNh_qXWMxfn5rBiHgkJhNE8t4C6ESW6miFfo6Hw.sditz_GeLXkkfKblos8X6hZlH3HWCHUnvJTwhCItYp4',
|
||||
'0xf622d6eC8a21532a62BA2CAFdda571c24D670E5c',
|
||||
sig
|
||||
);
|
||||
t.ok(verified, 'should verified signature');
|
||||
t.end();
|
||||
const verified = verifySignatureP256(
|
||||
'cl-5YNh_qXWMxfn5rBiHgkJhNE8t4C6ESW6miFfo6Hw.sditz_GeLXkkfKblos8X6hZlH3HWCHUnvJTwhCItYp4',
|
||||
'0xf622d6eC8a21532a62BA2CAFdda571c24D670E5c',
|
||||
sig
|
||||
);
|
||||
t.ok(verified, 'should verified signature');
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import EC from 'elliptic';
|
||||
|
||||
export function base64ToArrayBuffer(base64: string): ArrayBuffer {
|
||||
const binary_string = Buffer.from(
|
||||
base64.replace(/_/g, '/').replace(/-/g, '+'),
|
||||
'base64'
|
||||
).toString('binary');
|
||||
const len = binary_string.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
const binary_string = Buffer.from(
|
||||
base64.replace(/_/g, '/').replace(/-/g, '+'),
|
||||
'base64'
|
||||
).toString('binary');
|
||||
const len = binary_string.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
export function verifySignatureP256(pubkey: string, data: string, signature: string): boolean {
|
||||
const [x, y] = pubkey.split('.');
|
||||
const ec = new EC.ec('p256');
|
||||
const pub = ec.keyFromPublic({
|
||||
x: Buffer.from(base64ToArrayBuffer(x)).toString('hex'),
|
||||
y: Buffer.from(base64ToArrayBuffer(y)).toString('hex'),
|
||||
});
|
||||
return pub.verify(
|
||||
Buffer.from(data, 'utf-8').toString('hex'),
|
||||
Buffer.from(signature, 'hex').toJSON().data
|
||||
);
|
||||
const [x, y] = pubkey.split('.');
|
||||
const ec = new EC.ec('p256');
|
||||
const pub = ec.keyFromPublic({
|
||||
x: Buffer.from(base64ToArrayBuffer(x)).toString('hex'),
|
||||
y: Buffer.from(base64ToArrayBuffer(y)).toString('hex'),
|
||||
});
|
||||
return pub.verify(
|
||||
Buffer.from(data, 'utf-8').toString('hex'),
|
||||
Buffer.from(signature, 'hex').toJSON().data
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { interrepABI } from './abi';
|
||||
import config from './config';
|
||||
|
||||
const httpProvider = new Web3.providers.HttpProvider(
|
||||
'https://kovan.infura.io/v3/4ccf3cd743eb42b5a8cb1c8b0c0160ee'
|
||||
'https://kovan.infura.io/v3/4ccf3cd743eb42b5a8cb1c8b0c0160ee'
|
||||
);
|
||||
const web3 = new Web3(httpProvider);
|
||||
const interrep = new web3.eth.Contract(interrepABI as any, config.interrepContract);
|
||||
|
||||
@@ -3,26 +3,26 @@ const format = winston.format;
|
||||
const { combine, timestamp, prettyPrint } = format;
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: 'info',
|
||||
format: combine(timestamp(), format.colorize(), format.json()),
|
||||
transports: [
|
||||
new winston.transports.File({
|
||||
filename: 'error.log',
|
||||
level: 'error',
|
||||
}),
|
||||
new winston.transports.File({
|
||||
filename: 'combined.log',
|
||||
}),
|
||||
],
|
||||
level: 'info',
|
||||
format: combine(timestamp(), format.colorize(), format.json()),
|
||||
transports: [
|
||||
new winston.transports.File({
|
||||
filename: 'error.log',
|
||||
level: 'error',
|
||||
}),
|
||||
new winston.transports.File({
|
||||
filename: 'combined.log',
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
logger.add(
|
||||
new winston.transports.Console({
|
||||
level: 'error',
|
||||
format: winston.format.simple(),
|
||||
})
|
||||
);
|
||||
logger.add(
|
||||
new winston.transports.Console({
|
||||
level: 'error',
|
||||
format: winston.format.simple(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export default logger;
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
// @ts-ignore
|
||||
import tape from 'tape';
|
||||
import {
|
||||
Connection,
|
||||
ConnectionMessageSubType,
|
||||
MessageType,
|
||||
Moderation,
|
||||
ModerationMessageSubType,
|
||||
Post,
|
||||
PostMessageSubType,
|
||||
Profile,
|
||||
ProfileMessageSubType,
|
||||
Connection,
|
||||
ConnectionMessageSubType,
|
||||
MessageType,
|
||||
Moderation,
|
||||
ModerationMessageSubType,
|
||||
Post,
|
||||
PostMessageSubType,
|
||||
Profile,
|
||||
ProfileMessageSubType,
|
||||
} from './message';
|
||||
|
||||
tape('message.ts', async t => {
|
||||
const post1 = new Post({
|
||||
type: MessageType.Post,
|
||||
subtype: PostMessageSubType.Default,
|
||||
creator: '0xmyuser',
|
||||
payload: {
|
||||
content: 'hello world',
|
||||
},
|
||||
});
|
||||
const post1 = new Post({
|
||||
type: MessageType.Post,
|
||||
subtype: PostMessageSubType.Default,
|
||||
creator: '0xmyuser',
|
||||
payload: {
|
||||
content: 'hello world',
|
||||
},
|
||||
});
|
||||
|
||||
const mod1 = new Moderation({
|
||||
type: MessageType.Moderation,
|
||||
subtype: ModerationMessageSubType.Like,
|
||||
creator: '0xmyuser',
|
||||
payload: {
|
||||
reference: '0xmyuser/' + post1.hash(),
|
||||
},
|
||||
});
|
||||
const mod1 = new Moderation({
|
||||
type: MessageType.Moderation,
|
||||
subtype: ModerationMessageSubType.Like,
|
||||
creator: '0xmyuser',
|
||||
payload: {
|
||||
reference: '0xmyuser/' + post1.hash(),
|
||||
},
|
||||
});
|
||||
|
||||
const conn1 = new Connection({
|
||||
type: MessageType.Connection,
|
||||
subtype: ConnectionMessageSubType.Follow,
|
||||
creator: '0xmyuser',
|
||||
payload: {
|
||||
name: '0xotheruser',
|
||||
},
|
||||
});
|
||||
const conn1 = new Connection({
|
||||
type: MessageType.Connection,
|
||||
subtype: ConnectionMessageSubType.Follow,
|
||||
creator: '0xmyuser',
|
||||
payload: {
|
||||
name: '0xotheruser',
|
||||
},
|
||||
});
|
||||
|
||||
const pfp1 = new Profile({
|
||||
type: MessageType.Profile,
|
||||
subtype: ProfileMessageSubType.Name,
|
||||
creator: '0xmyuser',
|
||||
payload: {
|
||||
value: 'tsuk',
|
||||
},
|
||||
});
|
||||
const pfp1 = new Profile({
|
||||
type: MessageType.Profile,
|
||||
subtype: ProfileMessageSubType.Name,
|
||||
creator: '0xmyuser',
|
||||
payload: {
|
||||
value: 'tsuk',
|
||||
},
|
||||
});
|
||||
|
||||
t.deepEqual(
|
||||
Post.fromHex(post1.toHex()).toJSON(),
|
||||
post1.toJSON(),
|
||||
'should serialize and deserialize Post'
|
||||
);
|
||||
t.deepEqual(
|
||||
Post.fromHex(post1.toHex()).toJSON(),
|
||||
post1.toJSON(),
|
||||
'should serialize and deserialize Post'
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
Moderation.fromHex(mod1.toHex()).toJSON(),
|
||||
mod1.toJSON(),
|
||||
'should serialize and deserialize Moderation'
|
||||
);
|
||||
t.deepEqual(
|
||||
Moderation.fromHex(mod1.toHex()).toJSON(),
|
||||
mod1.toJSON(),
|
||||
'should serialize and deserialize Moderation'
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
Connection.fromHex(conn1.toHex()).toJSON(),
|
||||
conn1.toJSON(),
|
||||
'should serialize and deserialize Connection'
|
||||
);
|
||||
t.deepEqual(
|
||||
Connection.fromHex(conn1.toHex()).toJSON(),
|
||||
conn1.toJSON(),
|
||||
'should serialize and deserialize Connection'
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
Profile.fromHex(pfp1.toHex()).toJSON(),
|
||||
pfp1.toJSON(),
|
||||
'should serialize and deserialize Profile'
|
||||
);
|
||||
t.deepEqual(
|
||||
Profile.fromHex(pfp1.toHex()).toJSON(),
|
||||
pfp1.toJSON(),
|
||||
'should serialize and deserialize Profile'
|
||||
);
|
||||
|
||||
t.end();
|
||||
t.end();
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,32 +1,32 @@
|
||||
const mentionRegExp =
|
||||
'[' +
|
||||
'\\w-' +
|
||||
// Latin-1 Supplement (letters only) - https://en.wikipedia.org/wiki/List_of_Unicode_characters#Latin-1_Supplement
|
||||
'\u00C0-\u00D6' +
|
||||
'\u00D8-\u00F6' +
|
||||
'\u00F8-\u00FF' +
|
||||
// Latin Extended-A (without deprecated character) - https://en.wikipedia.org/wiki/List_of_Unicode_characters#Latin_Extended-A
|
||||
'\u0100-\u0148' +
|
||||
'\u014A-\u017F' +
|
||||
// Cyrillic symbols: \u0410-\u044F - https://en.wikipedia.org/wiki/Cyrillic_script_in_Unicode
|
||||
'\u0410-\u044F' +
|
||||
// hiragana (japanese): \u3040-\u309F - https://gist.github.com/ryanmcgrath/982242#file-japaneseregex-js
|
||||
'\u3040-\u309F' +
|
||||
// katakana (japanese): \u30A0-\u30FF - https://gist.github.com/ryanmcgrath/982242#file-japaneseregex-js
|
||||
'\u30A0-\u30FF' +
|
||||
// For an advanced explaination about Hangul see https://github.com/draft-js-plugins/draft-js-plugins/pull/480#issuecomment-254055437
|
||||
// Hangul Jamo (korean): \u3130-\u318F - https://en.wikipedia.org/wiki/Korean_language_and_computers#Hangul_in_Unicode
|
||||
// Hangul Syllables (korean): \uAC00-\uD7A3 - https://en.wikipedia.org/wiki/Korean_language_and_computers#Hangul_in_Unicode
|
||||
'\u3130-\u318F' +
|
||||
'\uAC00-\uD7A3' +
|
||||
// common chinese symbols: \u4e00-\u9eff - http://stackoverflow.com/a/1366113/837709
|
||||
// extended to \u9fa5 https://github.com/draft-js-plugins/draft-js-plugins/issues/1888
|
||||
'\u4e00-\u9fa5' +
|
||||
// Arabic https://en.wikipedia.org/wiki/Arabic_(Unicode_block)
|
||||
'\u0600-\u06ff' +
|
||||
// Vietnamese http://vietunicode.sourceforge.net/charset/
|
||||
'\u00C0-\u1EF9' +
|
||||
'\u002E' +
|
||||
']';
|
||||
'[' +
|
||||
'\\w-' +
|
||||
// Latin-1 Supplement (letters only) - https://en.wikipedia.org/wiki/List_of_Unicode_characters#Latin-1_Supplement
|
||||
'\u00C0-\u00D6' +
|
||||
'\u00D8-\u00F6' +
|
||||
'\u00F8-\u00FF' +
|
||||
// Latin Extended-A (without deprecated character) - https://en.wikipedia.org/wiki/List_of_Unicode_characters#Latin_Extended-A
|
||||
'\u0100-\u0148' +
|
||||
'\u014A-\u017F' +
|
||||
// Cyrillic symbols: \u0410-\u044F - https://en.wikipedia.org/wiki/Cyrillic_script_in_Unicode
|
||||
'\u0410-\u044F' +
|
||||
// hiragana (japanese): \u3040-\u309F - https://gist.github.com/ryanmcgrath/982242#file-japaneseregex-js
|
||||
'\u3040-\u309F' +
|
||||
// katakana (japanese): \u30A0-\u30FF - https://gist.github.com/ryanmcgrath/982242#file-japaneseregex-js
|
||||
'\u30A0-\u30FF' +
|
||||
// For an advanced explaination about Hangul see https://github.com/draft-js-plugins/draft-js-plugins/pull/480#issuecomment-254055437
|
||||
// Hangul Jamo (korean): \u3130-\u318F - https://en.wikipedia.org/wiki/Korean_language_and_computers#Hangul_in_Unicode
|
||||
// Hangul Syllables (korean): \uAC00-\uD7A3 - https://en.wikipedia.org/wiki/Korean_language_and_computers#Hangul_in_Unicode
|
||||
'\u3130-\u318F' +
|
||||
'\uAC00-\uD7A3' +
|
||||
// common chinese symbols: \u4e00-\u9eff - http://stackoverflow.com/a/1366113/837709
|
||||
// extended to \u9fa5 https://github.com/draft-js-plugins/draft-js-plugins/issues/1888
|
||||
'\u4e00-\u9fa5' +
|
||||
// Arabic https://en.wikipedia.org/wiki/Arabic_(Unicode_block)
|
||||
'\u0600-\u06ff' +
|
||||
// Vietnamese http://vietunicode.sourceforge.net/charset/
|
||||
'\u00C0-\u1EF9' +
|
||||
'\u002E' +
|
||||
']';
|
||||
export const HASHTAG_REGEX = /\#[\w\u0590-\u05ff]+/g;
|
||||
export const MENTION_REGEX = new RegExp(`\@${mentionRegExp}+`, 'g');
|
||||
|
||||
@@ -4,29 +4,29 @@ import { Dialect, Sequelize } from 'sequelize';
|
||||
let cached: Sequelize;
|
||||
|
||||
function getSequelize(): Sequelize {
|
||||
if (cached) return sequelize;
|
||||
if (cached) return sequelize;
|
||||
|
||||
if (!config.dbDialect || config.dbDialect === 'sqlite') {
|
||||
cached = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: config.dbStorage,
|
||||
logging: false,
|
||||
});
|
||||
} else {
|
||||
cached = new Sequelize(
|
||||
config.dbName as string,
|
||||
config.dbUsername as string,
|
||||
config.dbPassword,
|
||||
{
|
||||
host: config.dbHost,
|
||||
port: Number(config.dbPort),
|
||||
dialect: config.dbDialect as Dialect,
|
||||
logging: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
if (!config.dbDialect || config.dbDialect === 'sqlite') {
|
||||
cached = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: config.dbStorage,
|
||||
logging: false,
|
||||
});
|
||||
} else {
|
||||
cached = new Sequelize(
|
||||
config.dbName as string,
|
||||
config.dbUsername as string,
|
||||
config.dbPassword,
|
||||
{
|
||||
host: config.dbHost,
|
||||
port: Number(config.dbPort),
|
||||
dialect: config.dbDialect as Dialect,
|
||||
logging: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return cached;
|
||||
return cached;
|
||||
}
|
||||
|
||||
export const sequelize = getSequelize();
|
||||
|
||||
128
src/util/sse.ts
128
src/util/sse.ts
@@ -1,18 +1,18 @@
|
||||
import { Response } from 'express';
|
||||
|
||||
type SSEClient = {
|
||||
res: Response;
|
||||
lastUsed: number;
|
||||
res: Response;
|
||||
lastUsed: number;
|
||||
};
|
||||
|
||||
const CLIENT_CACHE: {
|
||||
[clientId: string]: SSEClient;
|
||||
[clientId: string]: SSEClient;
|
||||
} = {};
|
||||
|
||||
const TOPIC_MAP: {
|
||||
[topic: string]: {
|
||||
[clientId: string]: boolean;
|
||||
};
|
||||
[topic: string]: {
|
||||
[clientId: string]: boolean;
|
||||
};
|
||||
} = {};
|
||||
|
||||
const MAX_TTL = 2 * 60 * 1000;
|
||||
@@ -20,109 +20,109 @@ const MAX_TTL = 2 * 60 * 1000;
|
||||
let pruneTimeout: any;
|
||||
|
||||
export enum SSEType {
|
||||
INIT = 'INIT',
|
||||
NEW_CHAT_MESSAGE = 'NEW_CHAT_MESSAGE',
|
||||
HEALTHCHECK = 'HEALTHCHECK',
|
||||
INIT = 'INIT',
|
||||
NEW_CHAT_MESSAGE = 'NEW_CHAT_MESSAGE',
|
||||
HEALTHCHECK = 'HEALTHCHECK',
|
||||
}
|
||||
|
||||
export const addConnection = (clientId: string, res: Response) => {
|
||||
CLIENT_CACHE[clientId] = {
|
||||
res,
|
||||
lastUsed: Date.now(),
|
||||
};
|
||||
CLIENT_CACHE[clientId] = {
|
||||
res,
|
||||
lastUsed: Date.now(),
|
||||
};
|
||||
|
||||
const raw = `data: ${JSON.stringify({
|
||||
type: SSEType.INIT,
|
||||
clientId,
|
||||
})}\n\n`;
|
||||
const raw = `data: ${JSON.stringify({
|
||||
type: SSEType.INIT,
|
||||
clientId,
|
||||
})}\n\n`;
|
||||
|
||||
res.write(raw);
|
||||
res.write(raw);
|
||||
};
|
||||
|
||||
export const keepAlive = (clientId: string) => {
|
||||
const client = CLIENT_CACHE[clientId];
|
||||
const client = CLIENT_CACHE[clientId];
|
||||
|
||||
if (!client) throw new Error(`${clientId} not found`);
|
||||
if (!client) throw new Error(`${clientId} not found`);
|
||||
|
||||
client.lastUsed = Date.now();
|
||||
client.lastUsed = Date.now();
|
||||
};
|
||||
|
||||
export const addTopic = (clientId: string, topic: string) => {
|
||||
const client = CLIENT_CACHE[clientId];
|
||||
const client = CLIENT_CACHE[clientId];
|
||||
|
||||
if (!client) {
|
||||
throw new Error(`${clientId} not found`);
|
||||
}
|
||||
if (!client) {
|
||||
throw new Error(`${clientId} not found`);
|
||||
}
|
||||
|
||||
const bucket: { [id: string]: boolean } = TOPIC_MAP[topic] || {};
|
||||
bucket[clientId] = true;
|
||||
TOPIC_MAP[topic] = bucket;
|
||||
const bucket: { [id: string]: boolean } = TOPIC_MAP[topic] || {};
|
||||
bucket[clientId] = true;
|
||||
TOPIC_MAP[topic] = bucket;
|
||||
};
|
||||
|
||||
export const removeTopic = (clientId: string, topic: string) => {
|
||||
const client = CLIENT_CACHE[clientId];
|
||||
const client = CLIENT_CACHE[clientId];
|
||||
|
||||
if (!client) {
|
||||
throw new Error(`${clientId} not found`);
|
||||
}
|
||||
if (!client) {
|
||||
throw new Error(`${clientId} not found`);
|
||||
}
|
||||
|
||||
const bucket: { [id: string]: boolean } = TOPIC_MAP[topic] || {};
|
||||
const bucket: { [id: string]: boolean } = TOPIC_MAP[topic] || {};
|
||||
|
||||
if (bucket[clientId]) delete bucket[clientId];
|
||||
if (bucket[clientId]) delete bucket[clientId];
|
||||
|
||||
TOPIC_MAP[topic] = bucket;
|
||||
TOPIC_MAP[topic] = bucket;
|
||||
};
|
||||
|
||||
export const publishTopic = async (topic: string, data: any) => {
|
||||
const bucket: { [id: string]: boolean } = TOPIC_MAP[topic] || {};
|
||||
const bucket: { [id: string]: boolean } = TOPIC_MAP[topic] || {};
|
||||
|
||||
for (const clientId in bucket) {
|
||||
const client = CLIENT_CACHE[clientId];
|
||||
if (!client) {
|
||||
delete bucket[clientId];
|
||||
} else {
|
||||
const raw = `data: ${JSON.stringify(data)}\n\n`;
|
||||
client.res.write(raw);
|
||||
}
|
||||
for (const clientId in bucket) {
|
||||
const client = CLIENT_CACHE[clientId];
|
||||
if (!client) {
|
||||
delete bucket[clientId];
|
||||
} else {
|
||||
const raw = `data: ${JSON.stringify(data)}\n\n`;
|
||||
client.res.write(raw);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const removeConnection = (clientId: string) => {
|
||||
const client = CLIENT_CACHE[clientId];
|
||||
const client = CLIENT_CACHE[clientId];
|
||||
|
||||
if (!client) return;
|
||||
if (!client) return;
|
||||
|
||||
delete CLIENT_CACHE[clientId];
|
||||
delete CLIENT_CACHE[clientId];
|
||||
|
||||
client.res.end();
|
||||
client.res.end();
|
||||
};
|
||||
|
||||
export const getConnection = (clientId: string) => {
|
||||
return CLIENT_CACHE[clientId];
|
||||
return CLIENT_CACHE[clientId];
|
||||
};
|
||||
|
||||
export const pruneConnections = async () => {
|
||||
const now = Date.now();
|
||||
for (const clientId in CLIENT_CACHE) {
|
||||
const client = CLIENT_CACHE[clientId];
|
||||
if (now - client.lastUsed > MAX_TTL) {
|
||||
removeConnection(clientId);
|
||||
for (const topic in TOPIC_MAP) {
|
||||
const bucket = TOPIC_MAP[topic];
|
||||
if (bucket[clientId]) delete bucket[clientId];
|
||||
}
|
||||
}
|
||||
const now = Date.now();
|
||||
for (const clientId in CLIENT_CACHE) {
|
||||
const client = CLIENT_CACHE[clientId];
|
||||
if (now - client.lastUsed > MAX_TTL) {
|
||||
removeConnection(clientId);
|
||||
for (const topic in TOPIC_MAP) {
|
||||
const bucket = TOPIC_MAP[topic];
|
||||
if (bucket[clientId]) delete bucket[clientId];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const pruneLoop = async () => {
|
||||
if (pruneTimeout) {
|
||||
clearTimeout(pruneTimeout);
|
||||
}
|
||||
if (pruneTimeout) {
|
||||
clearTimeout(pruneTimeout);
|
||||
}
|
||||
|
||||
await pruneConnections();
|
||||
await pruneConnections();
|
||||
|
||||
setTimeout(pruneLoop, MAX_TTL);
|
||||
setTimeout(pruneLoop, MAX_TTL);
|
||||
};
|
||||
|
||||
pruneLoop();
|
||||
|
||||
@@ -3,17 +3,17 @@ import tape from 'tape';
|
||||
import { GenericService, MainService } from './svc';
|
||||
|
||||
class SampleService extends GenericService {
|
||||
return = (value: any) => value;
|
||||
return = (value: any) => value;
|
||||
|
||||
async start() {}
|
||||
async start() {}
|
||||
}
|
||||
|
||||
tape('svc.ts', async t => {
|
||||
const main = new MainService();
|
||||
main.add('sample', new SampleService());
|
||||
await main.start();
|
||||
const main = new MainService();
|
||||
main.add('sample', new SampleService());
|
||||
await main.start();
|
||||
|
||||
t.equal(await main.call('sample', 'return', 23), 23, 'should invoke service calls');
|
||||
t.equal(await main.call('sample', 'return', 23), 23, 'should invoke service calls');
|
||||
|
||||
t.end();
|
||||
t.end();
|
||||
});
|
||||
|
||||
158
src/util/svc.ts
158
src/util/svc.ts
@@ -3,99 +3,99 @@ import logger from './logger';
|
||||
let callerId = 0;
|
||||
|
||||
export class GenericService {
|
||||
name: string;
|
||||
main?: MainService;
|
||||
name: string;
|
||||
main?: MainService;
|
||||
|
||||
constructor() {
|
||||
this.name = '';
|
||||
}
|
||||
constructor() {
|
||||
this.name = '';
|
||||
}
|
||||
|
||||
async call(name: string, methodName: string, ...args: any[]) {
|
||||
const id = callerId++;
|
||||
// logger.debug(`called ${name}.${methodName}`, {
|
||||
// ...args,
|
||||
// origin: this.name,
|
||||
// id: id,
|
||||
// });
|
||||
async call(name: string, methodName: string, ...args: any[]) {
|
||||
const id = callerId++;
|
||||
// logger.debug(`called ${name}.${methodName}`, {
|
||||
// ...args,
|
||||
// origin: this.name,
|
||||
// id: id,
|
||||
// });
|
||||
|
||||
if (this.main) {
|
||||
const service: any = this.main.services[name];
|
||||
const method = service[methodName];
|
||||
if (typeof method === 'function') {
|
||||
try {
|
||||
const resp = await method.apply(service, args);
|
||||
// logger.debug(`handled ${name}.${methodName}`, {
|
||||
// origin: this.name,
|
||||
// id: id,
|
||||
// });
|
||||
return resp;
|
||||
} catch (e) {
|
||||
logger.error(e.message, {
|
||||
method: `${name}.${methodName}`,
|
||||
origin: this.name,
|
||||
id: id,
|
||||
});
|
||||
return Promise.reject(e);
|
||||
}
|
||||
} else {
|
||||
logger.error(`${name}.${methodName} is not a function`, {
|
||||
origin: this.name,
|
||||
id: id,
|
||||
});
|
||||
return Promise.reject(new Error(`${name}.${methodName} is not a function`));
|
||||
}
|
||||
}
|
||||
|
||||
logger.error('main service not found', {
|
||||
if (this.main) {
|
||||
const service: any = this.main.services[name];
|
||||
const method = service[methodName];
|
||||
if (typeof method === 'function') {
|
||||
try {
|
||||
const resp = await method.apply(service, args);
|
||||
// logger.debug(`handled ${name}.${methodName}`, {
|
||||
// origin: this.name,
|
||||
// id: id,
|
||||
// });
|
||||
return resp;
|
||||
} catch (e) {
|
||||
logger.error(e.message, {
|
||||
method: `${name}.${methodName}`,
|
||||
origin: this.name,
|
||||
id: id,
|
||||
});
|
||||
return Promise.reject(e);
|
||||
}
|
||||
} else {
|
||||
logger.error(`${name}.${methodName} is not a function`, {
|
||||
origin: this.name,
|
||||
id: id,
|
||||
});
|
||||
|
||||
return Promise.reject(new Error('Main service not found'));
|
||||
return Promise.reject(new Error(`${name}.${methodName} is not a function`));
|
||||
}
|
||||
}
|
||||
|
||||
async start() {}
|
||||
logger.error('main service not found', {
|
||||
origin: this.name,
|
||||
id: id,
|
||||
});
|
||||
|
||||
async stop() {}
|
||||
return Promise.reject(new Error('Main service not found'));
|
||||
}
|
||||
|
||||
async start() {}
|
||||
|
||||
async stop() {}
|
||||
}
|
||||
|
||||
export class MainService extends GenericService {
|
||||
services: {
|
||||
[name: string]: GenericService;
|
||||
};
|
||||
services: {
|
||||
[name: string]: GenericService;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.services = {};
|
||||
this.main = this;
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
this.services = {};
|
||||
this.main = this;
|
||||
}
|
||||
|
||||
add(name: string, service: GenericService): MainService {
|
||||
service.name = name;
|
||||
this.services[name] = service;
|
||||
service.main = this;
|
||||
logger.info(`added ${name}`, {
|
||||
service: name,
|
||||
add(name: string, service: GenericService): MainService {
|
||||
service.name = name;
|
||||
this.services[name] = service;
|
||||
service.main = this;
|
||||
logger.info(`added ${name}`, {
|
||||
service: name,
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
async start() {
|
||||
for (const name in this.services) {
|
||||
logger.info(`starting ${name}`, {
|
||||
service: name,
|
||||
});
|
||||
try {
|
||||
await this.services[name].start();
|
||||
logger.info(`started ${name}`, {
|
||||
service: name,
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
async start() {
|
||||
for (const name in this.services) {
|
||||
logger.info(`starting ${name}`, {
|
||||
service: name,
|
||||
});
|
||||
try {
|
||||
await this.services[name].start();
|
||||
logger.info(`started ${name}`, {
|
||||
service: name,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error(e.message, {
|
||||
service: name,
|
||||
});
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(e.message, {
|
||||
service: name,
|
||||
});
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,54 +12,54 @@ if (fs.existsSync(gunpath)) fs.unlinkSync(gunpath);
|
||||
if (fs.existsSync(dbpath)) fs.unlinkSync(dbpath);
|
||||
|
||||
export const getMockDB = async () => {
|
||||
if (!db) {
|
||||
db = new DBService();
|
||||
await db.start();
|
||||
if (!db) {
|
||||
db = new DBService();
|
||||
await db.start();
|
||||
|
||||
await parse('connections.csv', db.connections!.createConnection);
|
||||
await parse('ens.csv', ({ ens, address }) => db!.ens!.update(ens, address));
|
||||
await parse('interep_groups.csv', ({ root_hash, provider, name }) =>
|
||||
db!.interepGroups!.addHash(root_hash, provider, name)
|
||||
);
|
||||
await parse('links.csv', db.linkPreview!.update);
|
||||
await parse('meta.csv', db.meta!.update);
|
||||
await parse('moderations.csv', db.moderations!.createModeration);
|
||||
await parse('posts.csv', db.posts!.createPost);
|
||||
await parse('profiles.csv', db.profiles!.createProfile);
|
||||
await parse('semaphore_creators.csv', ({ group, provider, message_id }) =>
|
||||
db!.semaphoreCreators!.addSemaphoreCreator(message_id, provider, group)
|
||||
);
|
||||
await parse('semaphores.csv', ({ id_commitment, provider, name }) =>
|
||||
db!.semaphore!.addID(id_commitment, provider, name)
|
||||
);
|
||||
await parse('tags.csv', ({ tag_name, message_id }) =>
|
||||
db!.tags!.addTagPost(tag_name, message_id)
|
||||
);
|
||||
await parse('threads.csv', ({ root_id, message_id }) =>
|
||||
db!.threads!.addThreadData(root_id, message_id)
|
||||
);
|
||||
await parse('usermeta.csv', db.userMeta!.update);
|
||||
await parse('users.csv', db.users!.updateOrCreateUser);
|
||||
}
|
||||
await parse('connections.csv', db.connections!.createConnection);
|
||||
await parse('ens.csv', ({ ens, address }) => db!.ens!.update(ens, address));
|
||||
await parse('interep_groups.csv', ({ root_hash, provider, name }) =>
|
||||
db!.interepGroups!.addHash(root_hash, provider, name)
|
||||
);
|
||||
await parse('links.csv', db.linkPreview!.update);
|
||||
await parse('meta.csv', db.meta!.update);
|
||||
await parse('moderations.csv', db.moderations!.createModeration);
|
||||
await parse('posts.csv', db.posts!.createPost);
|
||||
await parse('profiles.csv', db.profiles!.createProfile);
|
||||
await parse('semaphore_creators.csv', ({ group, provider, message_id }) =>
|
||||
db!.semaphoreCreators!.addSemaphoreCreator(message_id, provider, group)
|
||||
);
|
||||
await parse('semaphores.csv', ({ id_commitment, provider, name }) =>
|
||||
db!.semaphore!.addID(id_commitment, provider, name)
|
||||
);
|
||||
await parse('tags.csv', ({ tag_name, message_id }) =>
|
||||
db!.tags!.addTagPost(tag_name, message_id)
|
||||
);
|
||||
await parse('threads.csv', ({ root_id, message_id }) =>
|
||||
db!.threads!.addThreadData(root_id, message_id)
|
||||
);
|
||||
await parse('usermeta.csv', db.userMeta!.update);
|
||||
await parse('users.csv', db.users!.updateOrCreateUser);
|
||||
}
|
||||
|
||||
return db;
|
||||
return db;
|
||||
};
|
||||
|
||||
const parse = async (filename: string, insertFn: (data: any) => any) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const buf = fs.readFileSync(path.join(process.cwd(), 'static', 'tests', filename));
|
||||
csv.parse(
|
||||
buf,
|
||||
{
|
||||
delimiter: ',',
|
||||
skip_empty_lines: true,
|
||||
columns: true,
|
||||
},
|
||||
async (err, rows) => {
|
||||
if (err) return reject(err);
|
||||
await Promise.all(rows.map((row: any) => insertFn(row)));
|
||||
resolve(rows);
|
||||
}
|
||||
);
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
const buf = fs.readFileSync(path.join(process.cwd(), 'static', 'tests', filename));
|
||||
csv.parse(
|
||||
buf,
|
||||
{
|
||||
delimiter: ',',
|
||||
skip_empty_lines: true,
|
||||
columns: true,
|
||||
},
|
||||
async (err, rows) => {
|
||||
if (err) return reject(err);
|
||||
await Promise.all(rows.map((row: any) => insertFn(row)));
|
||||
resolve(rows);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,307 +3,307 @@ import sinon, { SinonStub } from 'sinon';
|
||||
|
||||
let fetchStub: any;
|
||||
export const stubFetch = () => {
|
||||
// @ts-ignore
|
||||
fetchStub = fetchStub || sinon.stub(global, 'fetch');
|
||||
return fetchStub;
|
||||
// @ts-ignore
|
||||
fetchStub = fetchStub || sinon.stub(global, 'fetch');
|
||||
return fetchStub;
|
||||
};
|
||||
|
||||
export const stubCall = (
|
||||
service: any
|
||||
service: any
|
||||
): [
|
||||
SinonStub,
|
||||
{
|
||||
app: {
|
||||
read: SinonStub;
|
||||
updateLastArbitrumBlock: SinonStub;
|
||||
};
|
||||
ens: {
|
||||
update: SinonStub;
|
||||
};
|
||||
sempahore: {
|
||||
addID: SinonStub;
|
||||
removeID: SinonStub;
|
||||
findOneByCommitment: SinonStub;
|
||||
findAllByCommitment: SinonStub;
|
||||
};
|
||||
interepGroups: {
|
||||
findOneByHash: SinonStub;
|
||||
addHash: SinonStub;
|
||||
};
|
||||
users: {
|
||||
updateOrCreateUser: SinonStub;
|
||||
findOneByName: SinonStub;
|
||||
readAll: SinonStub;
|
||||
search: SinonStub;
|
||||
ensureUser: SinonStub;
|
||||
findOneByPubkey: SinonStub;
|
||||
};
|
||||
posts: {
|
||||
findLastTweetInConversation: SinonStub;
|
||||
findAllRepliesFromCreator: SinonStub;
|
||||
findAllLikedPostsByCreator: SinonStub;
|
||||
getHomeFeed: SinonStub;
|
||||
createTwitterPosts: SinonStub;
|
||||
findAllPosts: SinonStub;
|
||||
findAllReplies: SinonStub;
|
||||
findRoot: SinonStub;
|
||||
ensurePost: SinonStub;
|
||||
findOne: SinonStub;
|
||||
createPost: SinonStub;
|
||||
remove: SinonStub;
|
||||
};
|
||||
meta: {
|
||||
findTags: SinonStub;
|
||||
addPost: SinonStub;
|
||||
addReply: SinonStub;
|
||||
addRepost: SinonStub;
|
||||
addLike: SinonStub;
|
||||
removePost: SinonStub;
|
||||
removeReply: SinonStub;
|
||||
removeRepost: SinonStub;
|
||||
removeLike: SinonStub;
|
||||
};
|
||||
userMeta: {
|
||||
addPostingCount: SinonStub;
|
||||
addMentionedCount: SinonStub;
|
||||
addFollower: SinonStub;
|
||||
addFollowing: SinonStub;
|
||||
addBlocked: SinonStub;
|
||||
addBlocking: SinonStub;
|
||||
removePostingCount: SinonStub;
|
||||
removeMentionedCount: SinonStub;
|
||||
removeFollower: SinonStub;
|
||||
removeFollowing: SinonStub;
|
||||
removeBlocked: SinonStub;
|
||||
removeBlocking: SinonStub;
|
||||
};
|
||||
moderations: {
|
||||
findOne: SinonStub;
|
||||
createModeration: SinonStub;
|
||||
remove: SinonStub;
|
||||
};
|
||||
connections: {
|
||||
findOne: SinonStub;
|
||||
createConnection: SinonStub;
|
||||
remove: SinonStub;
|
||||
};
|
||||
profiles: {
|
||||
findOne: SinonStub;
|
||||
createProfile: SinonStub;
|
||||
remove: SinonStub;
|
||||
};
|
||||
semaphoreCreators: {
|
||||
addSemaphoreCreator: SinonStub;
|
||||
};
|
||||
tags: {
|
||||
getPostsByTag: SinonStub;
|
||||
addTagPost: SinonStub;
|
||||
removeTagPost: SinonStub;
|
||||
};
|
||||
threads: {
|
||||
addThreadData: SinonStub;
|
||||
};
|
||||
twitterAuths: {
|
||||
findUserByToken: SinonStub;
|
||||
addAccount: SinonStub;
|
||||
};
|
||||
link: {
|
||||
read: SinonStub;
|
||||
update: SinonStub;
|
||||
};
|
||||
merkle: {
|
||||
getGroupByRoot: SinonStub;
|
||||
};
|
||||
zkchat: {
|
||||
verifyRLNProof: SinonStub;
|
||||
checkShare: SinonStub;
|
||||
};
|
||||
}
|
||||
SinonStub,
|
||||
{
|
||||
app: {
|
||||
read: SinonStub;
|
||||
updateLastArbitrumBlock: SinonStub;
|
||||
};
|
||||
ens: {
|
||||
update: SinonStub;
|
||||
};
|
||||
sempahore: {
|
||||
addID: SinonStub;
|
||||
removeID: SinonStub;
|
||||
findOneByCommitment: SinonStub;
|
||||
findAllByCommitment: SinonStub;
|
||||
};
|
||||
interepGroups: {
|
||||
findOneByHash: SinonStub;
|
||||
addHash: SinonStub;
|
||||
};
|
||||
users: {
|
||||
updateOrCreateUser: SinonStub;
|
||||
findOneByName: SinonStub;
|
||||
readAll: SinonStub;
|
||||
search: SinonStub;
|
||||
ensureUser: SinonStub;
|
||||
findOneByPubkey: SinonStub;
|
||||
};
|
||||
posts: {
|
||||
findLastTweetInConversation: SinonStub;
|
||||
findAllRepliesFromCreator: SinonStub;
|
||||
findAllLikedPostsByCreator: SinonStub;
|
||||
getHomeFeed: SinonStub;
|
||||
createTwitterPosts: SinonStub;
|
||||
findAllPosts: SinonStub;
|
||||
findAllReplies: SinonStub;
|
||||
findRoot: SinonStub;
|
||||
ensurePost: SinonStub;
|
||||
findOne: SinonStub;
|
||||
createPost: SinonStub;
|
||||
remove: SinonStub;
|
||||
};
|
||||
meta: {
|
||||
findTags: SinonStub;
|
||||
addPost: SinonStub;
|
||||
addReply: SinonStub;
|
||||
addRepost: SinonStub;
|
||||
addLike: SinonStub;
|
||||
removePost: SinonStub;
|
||||
removeReply: SinonStub;
|
||||
removeRepost: SinonStub;
|
||||
removeLike: SinonStub;
|
||||
};
|
||||
userMeta: {
|
||||
addPostingCount: SinonStub;
|
||||
addMentionedCount: SinonStub;
|
||||
addFollower: SinonStub;
|
||||
addFollowing: SinonStub;
|
||||
addBlocked: SinonStub;
|
||||
addBlocking: SinonStub;
|
||||
removePostingCount: SinonStub;
|
||||
removeMentionedCount: SinonStub;
|
||||
removeFollower: SinonStub;
|
||||
removeFollowing: SinonStub;
|
||||
removeBlocked: SinonStub;
|
||||
removeBlocking: SinonStub;
|
||||
};
|
||||
moderations: {
|
||||
findOne: SinonStub;
|
||||
createModeration: SinonStub;
|
||||
remove: SinonStub;
|
||||
};
|
||||
connections: {
|
||||
findOne: SinonStub;
|
||||
createConnection: SinonStub;
|
||||
remove: SinonStub;
|
||||
};
|
||||
profiles: {
|
||||
findOne: SinonStub;
|
||||
createProfile: SinonStub;
|
||||
remove: SinonStub;
|
||||
};
|
||||
semaphoreCreators: {
|
||||
addSemaphoreCreator: SinonStub;
|
||||
};
|
||||
tags: {
|
||||
getPostsByTag: SinonStub;
|
||||
addTagPost: SinonStub;
|
||||
removeTagPost: SinonStub;
|
||||
};
|
||||
threads: {
|
||||
addThreadData: SinonStub;
|
||||
};
|
||||
twitterAuths: {
|
||||
findUserByToken: SinonStub;
|
||||
addAccount: SinonStub;
|
||||
};
|
||||
link: {
|
||||
read: SinonStub;
|
||||
update: SinonStub;
|
||||
};
|
||||
merkle: {
|
||||
getGroupByRoot: SinonStub;
|
||||
};
|
||||
zkchat: {
|
||||
verifyRLNProof: SinonStub;
|
||||
checkShare: SinonStub;
|
||||
};
|
||||
}
|
||||
] => {
|
||||
const callStub = sinon.stub(service, 'call');
|
||||
const callStub = sinon.stub(service, 'call');
|
||||
|
||||
const app = {
|
||||
updateLastArbitrumBlock: sinon.stub(),
|
||||
read: sinon.stub(),
|
||||
};
|
||||
const app = {
|
||||
updateLastArbitrumBlock: sinon.stub(),
|
||||
read: sinon.stub(),
|
||||
};
|
||||
|
||||
const ens = {
|
||||
update: sinon.stub(),
|
||||
};
|
||||
const ens = {
|
||||
update: sinon.stub(),
|
||||
};
|
||||
|
||||
const sempahore = {
|
||||
addID: sinon.stub(),
|
||||
removeID: sinon.stub(),
|
||||
findOneByCommitment: sinon.stub(),
|
||||
findAllByCommitment: sinon.stub(),
|
||||
};
|
||||
const sempahore = {
|
||||
addID: sinon.stub(),
|
||||
removeID: sinon.stub(),
|
||||
findOneByCommitment: sinon.stub(),
|
||||
findAllByCommitment: sinon.stub(),
|
||||
};
|
||||
|
||||
const interepGroups = {
|
||||
findOneByHash: sinon.stub(),
|
||||
addHash: sinon.stub(),
|
||||
};
|
||||
const interepGroups = {
|
||||
findOneByHash: sinon.stub(),
|
||||
addHash: sinon.stub(),
|
||||
};
|
||||
|
||||
const users = {
|
||||
findOneByName: sinon.stub(),
|
||||
readAll: sinon.stub(),
|
||||
search: sinon.stub(),
|
||||
ensureUser: sinon.stub(),
|
||||
findOneByPubkey: sinon.stub(),
|
||||
updateOrCreateUser: sinon.stub(),
|
||||
};
|
||||
const users = {
|
||||
findOneByName: sinon.stub(),
|
||||
readAll: sinon.stub(),
|
||||
search: sinon.stub(),
|
||||
ensureUser: sinon.stub(),
|
||||
findOneByPubkey: sinon.stub(),
|
||||
updateOrCreateUser: sinon.stub(),
|
||||
};
|
||||
|
||||
const posts = {
|
||||
findLastTweetInConversation: sinon.stub(),
|
||||
findAllRepliesFromCreator: sinon.stub(),
|
||||
findAllLikedPostsByCreator: sinon.stub(),
|
||||
getHomeFeed: sinon.stub(),
|
||||
findAllPosts: sinon.stub(),
|
||||
findAllReplies: sinon.stub(),
|
||||
createTwitterPosts: sinon.stub(),
|
||||
findRoot: sinon.stub(),
|
||||
ensurePost: sinon.stub(),
|
||||
findOne: sinon.stub(),
|
||||
createPost: sinon.stub(),
|
||||
remove: sinon.stub(),
|
||||
};
|
||||
const posts = {
|
||||
findLastTweetInConversation: sinon.stub(),
|
||||
findAllRepliesFromCreator: sinon.stub(),
|
||||
findAllLikedPostsByCreator: sinon.stub(),
|
||||
getHomeFeed: sinon.stub(),
|
||||
findAllPosts: sinon.stub(),
|
||||
findAllReplies: sinon.stub(),
|
||||
createTwitterPosts: sinon.stub(),
|
||||
findRoot: sinon.stub(),
|
||||
ensurePost: sinon.stub(),
|
||||
findOne: sinon.stub(),
|
||||
createPost: sinon.stub(),
|
||||
remove: sinon.stub(),
|
||||
};
|
||||
|
||||
const moderations = {
|
||||
findOne: sinon.stub(),
|
||||
createModeration: sinon.stub(),
|
||||
remove: sinon.stub(),
|
||||
};
|
||||
const moderations = {
|
||||
findOne: sinon.stub(),
|
||||
createModeration: sinon.stub(),
|
||||
remove: sinon.stub(),
|
||||
};
|
||||
|
||||
const connections = {
|
||||
findOne: sinon.stub(),
|
||||
createConnection: sinon.stub(),
|
||||
remove: sinon.stub(),
|
||||
};
|
||||
const connections = {
|
||||
findOne: sinon.stub(),
|
||||
createConnection: sinon.stub(),
|
||||
remove: sinon.stub(),
|
||||
};
|
||||
|
||||
const profiles = {
|
||||
findOne: sinon.stub(),
|
||||
createProfile: sinon.stub(),
|
||||
remove: sinon.stub(),
|
||||
};
|
||||
const profiles = {
|
||||
findOne: sinon.stub(),
|
||||
createProfile: sinon.stub(),
|
||||
remove: sinon.stub(),
|
||||
};
|
||||
|
||||
const meta = {
|
||||
findTags: sinon.stub(),
|
||||
addPost: sinon.stub(),
|
||||
addReply: sinon.stub(),
|
||||
addRepost: sinon.stub(),
|
||||
addLike: sinon.stub(),
|
||||
removePost: sinon.stub(),
|
||||
removeReply: sinon.stub(),
|
||||
removeRepost: sinon.stub(),
|
||||
removeLike: sinon.stub(),
|
||||
};
|
||||
const meta = {
|
||||
findTags: sinon.stub(),
|
||||
addPost: sinon.stub(),
|
||||
addReply: sinon.stub(),
|
||||
addRepost: sinon.stub(),
|
||||
addLike: sinon.stub(),
|
||||
removePost: sinon.stub(),
|
||||
removeReply: sinon.stub(),
|
||||
removeRepost: sinon.stub(),
|
||||
removeLike: sinon.stub(),
|
||||
};
|
||||
|
||||
const userMeta = {
|
||||
addPostingCount: sinon.stub(),
|
||||
addMentionedCount: sinon.stub(),
|
||||
addFollower: sinon.stub(),
|
||||
addFollowing: sinon.stub(),
|
||||
addBlocked: sinon.stub(),
|
||||
addBlocking: sinon.stub(),
|
||||
removePostingCount: sinon.stub(),
|
||||
removeMentionedCount: sinon.stub(),
|
||||
removeFollower: sinon.stub(),
|
||||
removeFollowing: sinon.stub(),
|
||||
removeBlocked: sinon.stub(),
|
||||
removeBlocking: sinon.stub(),
|
||||
};
|
||||
const userMeta = {
|
||||
addPostingCount: sinon.stub(),
|
||||
addMentionedCount: sinon.stub(),
|
||||
addFollower: sinon.stub(),
|
||||
addFollowing: sinon.stub(),
|
||||
addBlocked: sinon.stub(),
|
||||
addBlocking: sinon.stub(),
|
||||
removePostingCount: sinon.stub(),
|
||||
removeMentionedCount: sinon.stub(),
|
||||
removeFollower: sinon.stub(),
|
||||
removeFollowing: sinon.stub(),
|
||||
removeBlocked: sinon.stub(),
|
||||
removeBlocking: sinon.stub(),
|
||||
};
|
||||
|
||||
const semaphoreCreators = {
|
||||
addSemaphoreCreator: sinon.stub(),
|
||||
};
|
||||
const semaphoreCreators = {
|
||||
addSemaphoreCreator: sinon.stub(),
|
||||
};
|
||||
|
||||
const tags = {
|
||||
getPostsByTag: sinon.stub(),
|
||||
addTagPost: sinon.stub(),
|
||||
removeTagPost: sinon.stub(),
|
||||
};
|
||||
const tags = {
|
||||
getPostsByTag: sinon.stub(),
|
||||
addTagPost: sinon.stub(),
|
||||
removeTagPost: sinon.stub(),
|
||||
};
|
||||
|
||||
const threads = {
|
||||
addThreadData: sinon.stub(),
|
||||
};
|
||||
const threads = {
|
||||
addThreadData: sinon.stub(),
|
||||
};
|
||||
|
||||
const twitterAuths = {
|
||||
addAccount: sinon.stub(),
|
||||
findUserByToken: sinon.stub(),
|
||||
};
|
||||
const twitterAuths = {
|
||||
addAccount: sinon.stub(),
|
||||
findUserByToken: sinon.stub(),
|
||||
};
|
||||
|
||||
const link = {
|
||||
read: sinon.stub(),
|
||||
update: sinon.stub(),
|
||||
};
|
||||
const link = {
|
||||
read: sinon.stub(),
|
||||
update: sinon.stub(),
|
||||
};
|
||||
|
||||
const merkle = {
|
||||
getGroupByRoot: sinon.stub(),
|
||||
};
|
||||
const merkle = {
|
||||
getGroupByRoot: sinon.stub(),
|
||||
};
|
||||
|
||||
const zkchat = {
|
||||
verifyRLNProof: sinon.stub(),
|
||||
checkShare: sinon.stub(),
|
||||
};
|
||||
const zkchat = {
|
||||
verifyRLNProof: sinon.stub(),
|
||||
checkShare: sinon.stub(),
|
||||
};
|
||||
|
||||
callStub
|
||||
.withArgs('db', 'getSemaphore')
|
||||
.returns(Promise.resolve(sempahore))
|
||||
.withArgs('db', 'getInterepGroups')
|
||||
.returns(Promise.resolve(interepGroups))
|
||||
.withArgs('db', 'getUsers')
|
||||
.returns(Promise.resolve(users))
|
||||
.withArgs('db', 'getPosts')
|
||||
.returns(Promise.resolve(posts))
|
||||
.withArgs('db', 'getMeta')
|
||||
.returns(Promise.resolve(meta))
|
||||
.withArgs('db', 'getUserMeta')
|
||||
.returns(Promise.resolve(userMeta))
|
||||
.withArgs('db', 'getSemaphoreCreators')
|
||||
.returns(Promise.resolve(semaphoreCreators))
|
||||
.withArgs('db', 'getTags')
|
||||
.returns(Promise.resolve(tags))
|
||||
.withArgs('db', 'getThreads')
|
||||
.returns(Promise.resolve(threads))
|
||||
.withArgs('db', 'getModerations')
|
||||
.returns(Promise.resolve(moderations))
|
||||
.withArgs('db', 'getConnections')
|
||||
.returns(Promise.resolve(connections))
|
||||
.withArgs('db', 'getProfiles')
|
||||
.returns(Promise.resolve(profiles))
|
||||
.withArgs('db', 'getTwitterAuth')
|
||||
.returns(Promise.resolve(twitterAuths))
|
||||
.withArgs('db', 'getENS')
|
||||
.returns(Promise.resolve(ens))
|
||||
.withArgs('db', 'getApp')
|
||||
.returns(Promise.resolve(app))
|
||||
.withArgs('db', 'getLinkPreview')
|
||||
.returns(Promise.resolve(link))
|
||||
.withArgs('db', 'getMerkle')
|
||||
.returns(Promise.resolve(merkle))
|
||||
.withArgs('db', 'getZKChat')
|
||||
.returns(Promise.resolve(zkchat));
|
||||
callStub
|
||||
.withArgs('db', 'getSemaphore')
|
||||
.returns(Promise.resolve(sempahore))
|
||||
.withArgs('db', 'getInterepGroups')
|
||||
.returns(Promise.resolve(interepGroups))
|
||||
.withArgs('db', 'getUsers')
|
||||
.returns(Promise.resolve(users))
|
||||
.withArgs('db', 'getPosts')
|
||||
.returns(Promise.resolve(posts))
|
||||
.withArgs('db', 'getMeta')
|
||||
.returns(Promise.resolve(meta))
|
||||
.withArgs('db', 'getUserMeta')
|
||||
.returns(Promise.resolve(userMeta))
|
||||
.withArgs('db', 'getSemaphoreCreators')
|
||||
.returns(Promise.resolve(semaphoreCreators))
|
||||
.withArgs('db', 'getTags')
|
||||
.returns(Promise.resolve(tags))
|
||||
.withArgs('db', 'getThreads')
|
||||
.returns(Promise.resolve(threads))
|
||||
.withArgs('db', 'getModerations')
|
||||
.returns(Promise.resolve(moderations))
|
||||
.withArgs('db', 'getConnections')
|
||||
.returns(Promise.resolve(connections))
|
||||
.withArgs('db', 'getProfiles')
|
||||
.returns(Promise.resolve(profiles))
|
||||
.withArgs('db', 'getTwitterAuth')
|
||||
.returns(Promise.resolve(twitterAuths))
|
||||
.withArgs('db', 'getENS')
|
||||
.returns(Promise.resolve(ens))
|
||||
.withArgs('db', 'getApp')
|
||||
.returns(Promise.resolve(app))
|
||||
.withArgs('db', 'getLinkPreview')
|
||||
.returns(Promise.resolve(link))
|
||||
.withArgs('db', 'getMerkle')
|
||||
.returns(Promise.resolve(merkle))
|
||||
.withArgs('db', 'getZKChat')
|
||||
.returns(Promise.resolve(zkchat));
|
||||
|
||||
return [
|
||||
callStub,
|
||||
{
|
||||
app,
|
||||
ens,
|
||||
sempahore,
|
||||
interepGroups,
|
||||
users,
|
||||
posts,
|
||||
moderations,
|
||||
connections,
|
||||
meta,
|
||||
userMeta,
|
||||
semaphoreCreators,
|
||||
tags,
|
||||
threads,
|
||||
profiles,
|
||||
twitterAuths,
|
||||
link,
|
||||
merkle,
|
||||
zkchat,
|
||||
},
|
||||
];
|
||||
return [
|
||||
callStub,
|
||||
{
|
||||
app,
|
||||
ens,
|
||||
sempahore,
|
||||
interepGroups,
|
||||
users,
|
||||
posts,
|
||||
moderations,
|
||||
connections,
|
||||
meta,
|
||||
userMeta,
|
||||
semaphoreCreators,
|
||||
tags,
|
||||
threads,
|
||||
profiles,
|
||||
twitterAuths,
|
||||
link,
|
||||
merkle,
|
||||
zkchat,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@@ -7,67 +7,67 @@ import { stubFetch } from './testUtils';
|
||||
const fetchStub = stubFetch();
|
||||
|
||||
tape('twitter - requestToken', async t => {
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
status: 200,
|
||||
text: async () => 'token',
|
||||
})
|
||||
);
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
status: 200,
|
||||
text: async () => 'token',
|
||||
})
|
||||
);
|
||||
|
||||
const ret = await requestToken();
|
||||
const ret = await requestToken();
|
||||
|
||||
t.equal(
|
||||
fetchStub.args[0][0],
|
||||
'https://api.twitter.com/oauth/request_token',
|
||||
'should request token from twitter'
|
||||
);
|
||||
t.equal(
|
||||
fetchStub.args[0][0],
|
||||
'https://api.twitter.com/oauth/request_token',
|
||||
'should request token from twitter'
|
||||
);
|
||||
|
||||
t.equal(ret, 'token', 'should return text');
|
||||
t.equal(ret, 'token', 'should return text');
|
||||
|
||||
fetchStub.reset();
|
||||
t.end();
|
||||
fetchStub.reset();
|
||||
t.end();
|
||||
});
|
||||
|
||||
tape('twitter - accessToken', async t => {
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
status: 200,
|
||||
text: async () => 'token',
|
||||
})
|
||||
);
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
status: 200,
|
||||
text: async () => 'token',
|
||||
})
|
||||
);
|
||||
|
||||
const ret = await accessToken('1', '2', '3');
|
||||
const ret = await accessToken('1', '2', '3');
|
||||
|
||||
t.equal(
|
||||
fetchStub.args[0][0],
|
||||
'https://api.twitter.com/oauth/access_token',
|
||||
'should access token from twitter'
|
||||
);
|
||||
t.equal(
|
||||
fetchStub.args[0][0],
|
||||
'https://api.twitter.com/oauth/access_token',
|
||||
'should access token from twitter'
|
||||
);
|
||||
|
||||
t.equal(ret, 'token', 'should return text');
|
||||
t.equal(ret, 'token', 'should return text');
|
||||
|
||||
fetchStub.reset();
|
||||
t.end();
|
||||
fetchStub.reset();
|
||||
t.end();
|
||||
});
|
||||
|
||||
tape('twitter - verifyCredential', async t => {
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
status: 200,
|
||||
json: async () => 'token',
|
||||
})
|
||||
);
|
||||
fetchStub.returns(
|
||||
Promise.resolve({
|
||||
status: 200,
|
||||
json: async () => 'token',
|
||||
})
|
||||
);
|
||||
|
||||
const ret = await verifyCredential('1', '2');
|
||||
const ret = await verifyCredential('1', '2');
|
||||
|
||||
t.equal(
|
||||
fetchStub.args[0][0],
|
||||
'https://api.twitter.com/1.1/account/verify_credentials.json',
|
||||
'should access token from twitter'
|
||||
);
|
||||
t.equal(
|
||||
fetchStub.args[0][0],
|
||||
'https://api.twitter.com/1.1/account/verify_credentials.json',
|
||||
'should access token from twitter'
|
||||
);
|
||||
|
||||
t.equal(ret, 'token', 'should return text');
|
||||
t.equal(ret, 'token', 'should return text');
|
||||
|
||||
fetchStub.reset();
|
||||
t.end();
|
||||
fetchStub.reset();
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -16,259 +16,259 @@ const TW_ACCESS_KEY = config.twAccessKey;
|
||||
const TW_ACCESS_SECRET = config.twAccessSecret;
|
||||
|
||||
const botometer = new Botometer({
|
||||
consumerKey: TW_CONSUMER_KEY,
|
||||
consumerSecret: TW_CONSUMER_SECRET,
|
||||
accessToken: TW_ACCESS_KEY,
|
||||
accessTokenSecret: TW_ACCESS_SECRET,
|
||||
rapidApiKey: config.rapidAPIKey,
|
||||
usePro: true,
|
||||
consumerKey: TW_CONSUMER_KEY,
|
||||
consumerSecret: TW_CONSUMER_SECRET,
|
||||
accessToken: TW_ACCESS_KEY,
|
||||
accessTokenSecret: TW_ACCESS_SECRET,
|
||||
rapidApiKey: config.rapidAPIKey,
|
||||
usePro: true,
|
||||
});
|
||||
|
||||
const oauth = OAuth({
|
||||
consumer: {
|
||||
key: TW_CONSUMER_KEY,
|
||||
secret: TW_CONSUMER_SECRET,
|
||||
},
|
||||
signature_method: 'HMAC-SHA1',
|
||||
hash_function: (baseString: string, key: string) => {
|
||||
return crypto.createHmac('sha1', key).update(baseString).digest('base64');
|
||||
},
|
||||
consumer: {
|
||||
key: TW_CONSUMER_KEY,
|
||||
secret: TW_CONSUMER_SECRET,
|
||||
},
|
||||
signature_method: 'HMAC-SHA1',
|
||||
hash_function: (baseString: string, key: string) => {
|
||||
return crypto.createHmac('sha1', key).update(baseString).digest('base64');
|
||||
},
|
||||
});
|
||||
|
||||
export const createHeader = (requestData: any, key: string, secret: string) => {
|
||||
const headers = oauth.toHeader(
|
||||
oauth.authorize(requestData, {
|
||||
key: key,
|
||||
secret: secret,
|
||||
})
|
||||
);
|
||||
return headers;
|
||||
const headers = oauth.toHeader(
|
||||
oauth.authorize(requestData, {
|
||||
key: key,
|
||||
secret: secret,
|
||||
})
|
||||
);
|
||||
return headers;
|
||||
};
|
||||
|
||||
export const requestToken = async (): Promise<string> => {
|
||||
const requestData = {
|
||||
url: TW_REQ_TOKEN_URL,
|
||||
method: 'POST',
|
||||
data: {
|
||||
oauth_callbank: TW_CALLBACK_URL,
|
||||
},
|
||||
};
|
||||
const requestData = {
|
||||
url: TW_REQ_TOKEN_URL,
|
||||
method: 'POST',
|
||||
data: {
|
||||
oauth_callbank: TW_CALLBACK_URL,
|
||||
},
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const resp = await fetch(requestData.url, {
|
||||
method: requestData.method,
|
||||
form: requestData.data,
|
||||
headers: {
|
||||
...oauth.toHeader(oauth.authorize(requestData)),
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
// @ts-ignore
|
||||
const resp = await fetch(requestData.url, {
|
||||
method: requestData.method,
|
||||
form: requestData.data,
|
||||
headers: {
|
||||
...oauth.toHeader(oauth.authorize(requestData)),
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
|
||||
if (resp.status !== 200) throw new Error(resp.statusText);
|
||||
if (resp.status !== 200) throw new Error(resp.statusText);
|
||||
|
||||
return await resp.text();
|
||||
return await resp.text();
|
||||
};
|
||||
|
||||
export const accessToken = async (token: string, verifier: string, tokenSecret: string) => {
|
||||
const requestData = {
|
||||
url: TW_ACCESS_TOKEN_URL,
|
||||
method: 'POST',
|
||||
data: {
|
||||
oauth_token: token,
|
||||
oauth_verifier: verifier,
|
||||
oauth_token_secret: tokenSecret,
|
||||
},
|
||||
};
|
||||
const requestData = {
|
||||
url: TW_ACCESS_TOKEN_URL,
|
||||
method: 'POST',
|
||||
data: {
|
||||
oauth_token: token,
|
||||
oauth_verifier: verifier,
|
||||
oauth_token_secret: tokenSecret,
|
||||
},
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const resp = await fetch(requestData.url, {
|
||||
method: requestData.method,
|
||||
form: requestData.data,
|
||||
headers: {
|
||||
...oauth.toHeader(oauth.authorize(requestData)),
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
// @ts-ignore
|
||||
const resp = await fetch(requestData.url, {
|
||||
method: requestData.method,
|
||||
form: requestData.data,
|
||||
headers: {
|
||||
...oauth.toHeader(oauth.authorize(requestData)),
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
|
||||
if (resp.status !== 200) throw new Error(resp.statusText);
|
||||
if (resp.status !== 200) throw new Error(resp.statusText);
|
||||
|
||||
return await resp.text();
|
||||
return await resp.text();
|
||||
};
|
||||
|
||||
export const verifyCredential = async (key: string, secret: string) => {
|
||||
const headers = oauth.toHeader(
|
||||
oauth.authorize(
|
||||
{
|
||||
url: `https://api.twitter.com/1.1/account/verify_credentials.json`,
|
||||
method: 'GET',
|
||||
},
|
||||
{
|
||||
key: key,
|
||||
secret: secret,
|
||||
}
|
||||
)
|
||||
);
|
||||
const headers = oauth.toHeader(
|
||||
oauth.authorize(
|
||||
{
|
||||
url: `https://api.twitter.com/1.1/account/verify_credentials.json`,
|
||||
method: 'GET',
|
||||
},
|
||||
{
|
||||
key: key,
|
||||
secret: secret,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
const resp = await fetch(`https://api.twitter.com/1.1/account/verify_credentials.json`, {
|
||||
headers: headers,
|
||||
});
|
||||
// @ts-ignore
|
||||
const resp = await fetch(`https://api.twitter.com/1.1/account/verify_credentials.json`, {
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
if (resp.status !== 200) {
|
||||
throw new Error(resp.statusText);
|
||||
}
|
||||
if (resp.status !== 200) {
|
||||
throw new Error(resp.statusText);
|
||||
}
|
||||
|
||||
const json = await resp.json();
|
||||
const json = await resp.json();
|
||||
|
||||
return json;
|
||||
return json;
|
||||
};
|
||||
|
||||
export const updateStatus = async (
|
||||
status: string,
|
||||
in_reply_to_status_id: string,
|
||||
key: string,
|
||||
secret: string
|
||||
status: string,
|
||||
in_reply_to_status_id: string,
|
||||
key: string,
|
||||
secret: string
|
||||
) => {
|
||||
const requestData = {
|
||||
url: `https://api.twitter.com/1.1/statuses/update.json`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
status,
|
||||
in_reply_to_status_id,
|
||||
},
|
||||
};
|
||||
const headers = oauth.toHeader(
|
||||
oauth.authorize(requestData, {
|
||||
key: key,
|
||||
secret: secret,
|
||||
})
|
||||
);
|
||||
const requestData = {
|
||||
url: `https://api.twitter.com/1.1/statuses/update.json`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
status,
|
||||
in_reply_to_status_id,
|
||||
},
|
||||
};
|
||||
const headers = oauth.toHeader(
|
||||
oauth.authorize(requestData, {
|
||||
key: key,
|
||||
secret: secret,
|
||||
})
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
const resp = await fetch(requestData.url, {
|
||||
method: requestData.method,
|
||||
body: new URLSearchParams(requestData.data).toString(),
|
||||
headers: {
|
||||
...headers,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
const json = await resp.json();
|
||||
// @ts-ignore
|
||||
const resp = await fetch(requestData.url, {
|
||||
method: requestData.method,
|
||||
body: new URLSearchParams(requestData.data).toString(),
|
||||
headers: {
|
||||
...headers,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
const json = await resp.json();
|
||||
|
||||
if (resp.status === 200) {
|
||||
return json;
|
||||
} else {
|
||||
throw new Error(json.errors[0].message);
|
||||
}
|
||||
if (resp.status === 200) {
|
||||
return json;
|
||||
} else {
|
||||
throw new Error(json.errors[0].message);
|
||||
}
|
||||
};
|
||||
|
||||
export async function showStatus(id: string, key?: string, secret?: string) {
|
||||
const requestData = {
|
||||
url: `https://api.twitter.com/1.1/statuses/show/${id}.json`,
|
||||
method: 'GET',
|
||||
};
|
||||
const headers = oauth.toHeader(
|
||||
oauth.authorize(requestData, {
|
||||
key: key || TW_ACCESS_KEY,
|
||||
secret: secret || TW_ACCESS_SECRET,
|
||||
})
|
||||
);
|
||||
const requestData = {
|
||||
url: `https://api.twitter.com/1.1/statuses/show/${id}.json`,
|
||||
method: 'GET',
|
||||
};
|
||||
const headers = oauth.toHeader(
|
||||
oauth.authorize(requestData, {
|
||||
key: key || TW_ACCESS_KEY,
|
||||
secret: secret || TW_ACCESS_SECRET,
|
||||
})
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
const resp = await fetch(requestData.url, {
|
||||
method: requestData.method,
|
||||
headers: {
|
||||
...headers,
|
||||
},
|
||||
});
|
||||
// @ts-ignore
|
||||
const resp = await fetch(requestData.url, {
|
||||
method: requestData.method,
|
||||
headers: {
|
||||
...headers,
|
||||
},
|
||||
});
|
||||
|
||||
if (resp.status !== 200) {
|
||||
throw new Error(resp.statusText);
|
||||
}
|
||||
if (resp.status !== 200) {
|
||||
throw new Error(resp.statusText);
|
||||
}
|
||||
|
||||
const json = await resp.json();
|
||||
const json = await resp.json();
|
||||
|
||||
return json;
|
||||
return json;
|
||||
}
|
||||
|
||||
export async function getReplies(tweetUrl: string, lastTweetHash?: string): Promise<PostModel[]> {
|
||||
const [__, _, tweetId] = tweetUrl.replace('https://twitter.com/', '').split('/');
|
||||
const [__, _, tweetId] = tweetUrl.replace('https://twitter.com/', '').split('/');
|
||||
|
||||
const sinceId = lastTweetHash ? `&since_id=${lastTweetHash}` : '';
|
||||
const qs =
|
||||
'&max_results=100&expansions=author_id,in_reply_to_user_id&tweet.fields=referenced_tweets,in_reply_to_user_id,author_id,created_at,conversation_id&user.fields=name,username';
|
||||
const sinceId = lastTweetHash ? `&since_id=${lastTweetHash}` : '';
|
||||
const qs =
|
||||
'&max_results=100&expansions=author_id,in_reply_to_user_id&tweet.fields=referenced_tweets,in_reply_to_user_id,author_id,created_at,conversation_id&user.fields=name,username';
|
||||
|
||||
// @ts-ignore
|
||||
const resp = await fetch(
|
||||
`https://api.twitter.com/2/tweets/search/recent?query=conversation_id:${tweetId}${sinceId}${qs}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${TW_BEARER_TOKEN}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
// @ts-ignore
|
||||
const resp = await fetch(
|
||||
`https://api.twitter.com/2/tweets/search/recent?query=conversation_id:${tweetId}${sinceId}${qs}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${TW_BEARER_TOKEN}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const json = await resp.json();
|
||||
const json = await resp.json();
|
||||
|
||||
if (json.errors) return [];
|
||||
if (json.errors) return [];
|
||||
|
||||
const data = json.data || [];
|
||||
const users = (json.includes?.users || []).reduce((acc: any, user: any) => {
|
||||
acc[user.id] = user.username;
|
||||
return acc;
|
||||
}, {});
|
||||
const data = json.data || [];
|
||||
const users = (json.includes?.users || []).reduce((acc: any, user: any) => {
|
||||
acc[user.id] = user.username;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return data.map(
|
||||
(tweet: {
|
||||
author_id: string;
|
||||
created_at: string;
|
||||
coversation_id: string;
|
||||
in_reply_to_user_id: string;
|
||||
text: string;
|
||||
id: string;
|
||||
referenced_tweets: { type: string; id: string }[];
|
||||
}): PostModel => {
|
||||
const reply = tweet.referenced_tweets.filter(({ type }) => type === 'replied_to')[0];
|
||||
return {
|
||||
messageId: tweet.id,
|
||||
hash: tweet.id,
|
||||
creator: users[tweet.author_id] || tweet.author_id,
|
||||
type: '@TWEET@',
|
||||
subtype: '',
|
||||
createdAt: new Date(tweet.created_at).getTime(),
|
||||
topic: '',
|
||||
title: '',
|
||||
content: tweet.text,
|
||||
reference: reply ? reply.id : tweetId,
|
||||
attachment: '',
|
||||
};
|
||||
}
|
||||
);
|
||||
return data.map(
|
||||
(tweet: {
|
||||
author_id: string;
|
||||
created_at: string;
|
||||
coversation_id: string;
|
||||
in_reply_to_user_id: string;
|
||||
text: string;
|
||||
id: string;
|
||||
referenced_tweets: { type: string; id: string }[];
|
||||
}): PostModel => {
|
||||
const reply = tweet.referenced_tweets.filter(({ type }) => type === 'replied_to')[0];
|
||||
return {
|
||||
messageId: tweet.id,
|
||||
hash: tweet.id,
|
||||
creator: users[tweet.author_id] || tweet.author_id,
|
||||
type: '@TWEET@',
|
||||
subtype: '',
|
||||
createdAt: new Date(tweet.created_at).getTime(),
|
||||
topic: '',
|
||||
title: '',
|
||||
content: tweet.text,
|
||||
reference: reply ? reply.id : tweetId,
|
||||
attachment: '',
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function getUser(username: string): Promise<{
|
||||
id: string;
|
||||
name: string;
|
||||
username: string;
|
||||
profile_image_url: string;
|
||||
id: string;
|
||||
name: string;
|
||||
username: string;
|
||||
profile_image_url: string;
|
||||
} | null> {
|
||||
const qs = '?user.fields=name,username,profile_image_url';
|
||||
const qs = '?user.fields=name,username,profile_image_url';
|
||||
|
||||
// @ts-ignore
|
||||
const resp = await fetch(`https://api.twitter.com/2/users/by/username/${username}${qs}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${TW_BEARER_TOKEN}`,
|
||||
},
|
||||
});
|
||||
// @ts-ignore
|
||||
const resp = await fetch(`https://api.twitter.com/2/users/by/username/${username}${qs}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${TW_BEARER_TOKEN}`,
|
||||
},
|
||||
});
|
||||
|
||||
const json = await resp.json();
|
||||
if (json.errors) return null;
|
||||
const json = await resp.json();
|
||||
if (json.errors) return null;
|
||||
|
||||
return json.data;
|
||||
return json.data;
|
||||
}
|
||||
|
||||
export async function getBotometerScore(username: string): Promise<any> {
|
||||
return botometer.getScore(username);
|
||||
return botometer.getScore(username);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user