This commit is contained in:
ADAMJR 2021-10-24 16:54:25 +01:00
parent 05554fe7ca
commit 387ece8b2f
7 changed files with 902 additions and 1086 deletions

View File

@ -14,7 +14,12 @@ Custom Frontend and Backend that is similar to Discord.
1. Clone the repo.
2. Generate SSH keys.
From app folder: `mkdir -p backend/keys && ssh-keygen -t rsa -b 2048 -m PEM -f backend/keys/accord.app`
From app folder:
```
mkdir -p backend/keys
&& ssh-keygen -t rsa -b 2048 -m PEM -f backend/keys/jwt
&& openssl rand -rand /dev/urandom 256 > backend/keys/message
```
3. Install npm packages.
From app folder: `cd frontend && npm i && cd ../backend && npm i`

1879
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@
"chai-things": "^0.2.0",
"colors": "^1.4.0",
"cors": "^2.8.5",
"crypto-js": "^4.1.1",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"express-async-errors": "^3.1.1",
@ -37,7 +38,8 @@
"socket.io": "^4.0.0",
"socket.io-client": "^4.0.0",
"ts-node": "^9.1.1",
"typescript": "^4.2.3"
"typescript": "^4.2.3",
"un": "^0.0.0"
},
"devDependencies": {
"@types/chai": "^4.2.14",
@ -46,6 +48,7 @@
"@types/chai-things": "^0.0.34",
"@types/colors": "^1.2.1",
"@types/cors": "^2.8.7",
"@types/crypto-js": "^4.0.2",
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.11",
"@types/express-rate-limit": "^5.1.1",

View File

@ -1,8 +1,12 @@
import got from 'got/dist/source';
import DBWrapper from './db-wrapper';
import { Channel } from './models/channel';
import { Message, MessageDocument } from './models/message';
import { generateSnowflake } from './snowflake-entity';
import AES from 'crypto-js/aes';
import { readFileSync } from 'fs';
import { resolve } from 'path';
const messageKey = readFileSync(resolve(`./keys/message`), 'utf-8');
export default class Messages extends DBWrapper<string, MessageDocument> {
public async get(id: string | undefined) {
@ -20,7 +24,7 @@ export default class Messages extends DBWrapper<string, MessageDocument> {
_id: generateSnowflake(),
authorId,
channelId,
content,
encryptedContent: AES.encrypt(content, messageKey).ciphertext.words,
});
}

View File

@ -2,11 +2,18 @@ import { Document, model, Schema } from 'mongoose';
import patterns from '../../types/patterns';
import { createdAtToDate, useId } from '../../utils/utils';
import { generateSnowflake } from '../snowflake-entity';
import AES from 'crypto-js/aes';
import { readFileSync } from 'fs';
import { resolve } from 'path';
const messageKey = readFileSync(resolve(`./keys/message`), 'utf-8');
export interface MessageDocument extends Document, Entity.Message {
_id: string | never;
id: string;
createdAt: never;
encryptedContent: number[];
content: never;
}
export const Message = model<MessageDocument>('message', new Schema({
@ -24,6 +31,8 @@ export const Message = model<MessageDocument>('message', new Schema({
required: [true, 'Channel ID is required'],
validate: [patterns.snowflake, 'Invalid Snowflake ID'],
},
encryptedContent: { type: Array },
// TODO: eventually remove
content: {
type: String,
minlength: [1, 'Content too short'],
@ -40,4 +49,16 @@ export const Message = model<MessageDocument>('message', new Schema({
url: String,
}),
updatedAt: Date,
}, { toJSON: { getters: true } }).method('toClient', useId));
}, { toJSON: { getters: true } })
// TODO: refactor
.method('toClient', function(this: MessageDocument) {
const obj = this.toObject();
this.id = this._id;
delete this._id;
this.content = AES.decrypt(this['encryptedContent'], messageKey);
delete this.encryptedContent;
return obj;
}));

View File

@ -68,7 +68,7 @@ export default class Users extends DBWrapper<string, UserDocument> {
public async createToken(user: SelfUserDocument, expire = true) {
// too insecure to keep in memory
const key = await readFileAsync('./keys/accord.app', { encoding: 'utf-8' });
const key = await readFileAsync('./keys/jwt', { encoding: 'utf-8' });
return jwt.sign(
{ id: user.id },
key,
@ -81,7 +81,7 @@ export default class Users extends DBWrapper<string, UserDocument> {
}
public async verifyToken(token: string | undefined): Promise<string> {
// too insecure to keep in memory
const key = await readFileAsync('./keys/accord.app', { encoding: 'utf-8' });
const key = await readFileAsync('./keys/jwt', { encoding: 'utf-8' });
const decoded = jwt.verify(token as string, key, { algorithms: ['RS256'] }) as UserToken;
return decoded?.id;
}

View File

@ -37,40 +37,40 @@ console.log(`${space(48 * 3)}TESTS${space(54 * 2)}`.bgWhite.black);
execSync(`kill -9 $(lsof -i :${process.env.PORT} | tail -n 1 | cut -d ' ' -f5) 2>> /dev/null`);
} catch {}
// import('./integration/routes/auth-routes.tests');
// import('./integration/routes/invites-routes.tests');
// import('./integration/routes/guilds-routes.tests');
// import('./integration/routes/channel-routes.tests');
import('./integration/routes/auth-routes.tests');
import('./integration/routes/invites-routes.tests');
import('./integration/routes/guilds-routes.tests');
import('./integration/routes/channel-routes.tests');
// import('./integration/ws/channel-create.tests');
// // TODO: import('./integration/ws/channel-delete.tests');
// // TODO: import('./integration/ws/channel-update.tests');
// import('./integration/ws/guild-member-add.tests'); //fail
// import('./integration/ws/guild-member-remove.tests'); //fail
// import('./integration/ws/guild-member-update.tests'); // fail
// import('./integration/ws/guild-create.tests'); // fail
// import('./integration/ws/guild-delete.tests'); // fail
// import('./integration/ws/guild-update.tests'); // fail
// import('./integration/ws/invite-create.tests'); //fail
// import('./integration/ws/invite-delete.tests'); //fail
// import('./integration/ws/message-create.tests'); //fail
// import('./integration/ws/message-update.tests'); // fail
// import('./integration/ws/message-delete.tests'); // fail
// import('./integration/ws/ready.tests');
// import('./integration/ws/user-update.tests');
// import('./integration/ws/ws-guard.tests');
import('./integration/ws/channel-create.tests');
// TODO: import('./integration/ws/channel-delete.tests');
// TODO: import('./integration/ws/channel-update.tests');
import('./integration/ws/guild-member-add.tests'); //fail
import('./integration/ws/guild-member-remove.tests'); //fail
import('./integration/ws/guild-member-update.tests'); // fail
import('./integration/ws/guild-create.tests'); // fail
import('./integration/ws/guild-delete.tests'); // fail
import('./integration/ws/guild-update.tests'); // fail
import('./integration/ws/invite-create.tests'); //fail
import('./integration/ws/invite-delete.tests'); //fail
import('./integration/ws/message-create.tests'); //fail
import('./integration/ws/message-update.tests'); // fail
import('./integration/ws/message-delete.tests'); // fail
import('./integration/ws/ready.tests');
import('./integration/ws/user-update.tests');
import('./integration/ws/ws-guard.tests');
import('./integration/data/roles.tests');
// import('./unit/models/app.tests');
// import('./unit/models/channel.tests');
// import('./unit/models/guild.tests');
// import('./unit/models/guild-member.tests');
// import('./unit/models/invite.tests');
// import('./unit/models/message.tests');
// import('./unit/models/role.tests');
// import('./unit/models/user.tests');
// import('./unit/snowflake-entity.tests');
// import('./unit/ws/ws-cooldowns.tests');
import('./unit/models/app.tests');
import('./unit/models/channel.tests');
import('./unit/models/guild.tests');
import('./unit/models/guild-member.tests');
import('./unit/models/invite.tests');
import('./unit/models/message.tests');
import('./unit/models/role.tests');
import('./unit/models/user.tests');
import('./unit/snowflake-entity.tests');
import('./unit/ws/ws-cooldowns.tests');
})();
// needs to be here, or tests won't run