Dockerize Application

This commit is contained in:
ADAMJR 2021-11-30 02:28:25 +00:00
parent 1d7d14bb85
commit e9bc2958ad
66 changed files with 359 additions and 140 deletions

View File

@ -1,6 +1,12 @@
.env
keys/
node_modules/
lib/
logs/
upload/
.DS_STORE
node_modules
scripts/flow/*/.flowconfig
.flowconfig
*~

1
CNAME
View File

@ -1 +0,0 @@
accord.app

View File

@ -61,3 +61,31 @@ These setup instructions are in a primitive state, and may be improved in the fu
> Want a more basic version, that's more like Discord?
> https://github.com/codea-live/dclone
---
`backend/.env`
(dev with Docker)
```
EMAIL_ADDRESS="...@gmail.com"
EMAIL_PASSWORD="..."
MONGO_URI="mongodb://database/accord"
NODE_ENV="dev"
PORT=3000
WEBSITE_URL="http://localhost:4200"
```
`backend/test/.env`
(test without Docker)
```
API_URL="http://localhost:3001/api"
EMAIL_ADDRESS="...@gmail.com"
EMAIL_PASSWORD="..."
MONGO_URI="mongodb://localhost/accord-test"
NODE_ENV="dev"
PORT=3001
ROOT_ENDPOINT="http://localhost:3001"
WEBSITE_URL="http://localhost:4200"
```

1
backend/.dockerignore Symbolic link
View File

@ -0,0 +1 @@
../.gitignore

View File

@ -1,7 +0,0 @@
API_URL="http://localhost:3000"
EMAIL_ADDRESS="example@gmail.com"
EMAIL_PASSWORD="google_account_password"
MONGO_URI="mongodb://localhost/accord"
PORT=3000
ROOT_ENDPOINT="http://localhost:3000"
WEBSITE_URL="http://localhost:4200"

7
backend/.gitignore vendored
View File

@ -1,7 +0,0 @@
.env
keys/
node_modules/
lib/
logs/
upload/

13
backend/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
FROM node:16-alpine3.14
RUN addgroup app && adduser -SG app app
WORKDIR /app
COPY --chown=app:app package*.json ./
RUN npm i
COPY . .
RUN unlink /app/src/types
COPY --chown=app:app --from=accord_types:latest /app /app/src/types
EXPOSE 3000
USER app
CMD npm run start:dev

View File

@ -7,9 +7,9 @@
"": {
"name": "accord-backend",
"version": "0.0.0",
"license": "ISC",
"dependencies": {
"@accord/ion": "github:accord-dot-app/ion",
"@accord/types": "file:../types",
"body-parser": "^1.19.0",
"chai-things": "^0.2.0",
"colors": "^1.4.0",
@ -75,12 +75,14 @@
"mocha": "^8.2.1",
"nodemon": "^2.0.14",
"supertest": "^6.1.3",
"ts-mocha": "^8.0.0"
"ts-mocha": "^8.0.0",
"ts-node-dev": "^1.1.8"
}
},
"../types": {},
"node_modules/@accord/ion": {
"version": "1.0.1",
"resolved": "git+ssh://git@github.com/accord-dot-app/ion.git#c77ed01bbea93eeeff858004a7f6c6287a6d43de",
"version": "1.0.2",
"resolved": "git+ssh://git@github.com/accord-dot-app/ion.git#ea5ce8d2e2073e0260476f0ec96c6603fc502fd3",
"hasInstallScript": true,
"license": "ISC",
"dependencies": {
@ -276,6 +278,10 @@
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz",
"integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw=="
},
"node_modules/@accord/types": {
"resolved": "../types",
"link": true
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
@ -829,6 +835,18 @@
"@types/node": "*"
}
},
"node_modules/@types/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=",
"dev": true
},
"node_modules/@types/strip-json-comments": {
"version": "0.0.30",
"resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz",
"integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==",
"dev": true
},
"node_modules/@types/superagent": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.10.tgz",
@ -2105,6 +2123,15 @@
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
"dev": true
},
"node_modules/dynamic-dedupe": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz",
"integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=",
"dev": true,
"dependencies": {
"xtend": "^4.0.0"
}
},
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@ -5640,7 +5667,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"optional": true,
"devOptional": true,
"engines": {
"node": ">=4"
}
@ -5946,6 +5973,15 @@
"node": ">=12"
}
},
"node_modules/tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"dev": true,
"bin": {
"tree-kill": "cli.js"
}
},
"node_modules/triple-beam": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
@ -6041,6 +6077,64 @@
"node": ">=10.0.0"
}
},
"node_modules/ts-node-dev": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.8.tgz",
"integrity": "sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg==",
"dev": true,
"dependencies": {
"chokidar": "^3.5.1",
"dynamic-dedupe": "^0.3.0",
"minimist": "^1.2.5",
"mkdirp": "^1.0.4",
"resolve": "^1.0.0",
"rimraf": "^2.6.1",
"source-map-support": "^0.5.12",
"tree-kill": "^1.2.2",
"ts-node": "^9.0.0",
"tsconfig": "^7.0.0"
},
"bin": {
"ts-node-dev": "lib/bin.js",
"tsnd": "lib/bin.js"
},
"engines": {
"node": ">=0.8.0"
},
"peerDependencies": {
"node-notifier": "*",
"typescript": "*"
},
"peerDependenciesMeta": {
"node-notifier": {
"optional": true
}
}
},
"node_modules/ts-node-dev/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/tsconfig": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz",
"integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==",
"dev": true,
"dependencies": {
"@types/strip-bom": "^3.0.0",
"@types/strip-json-comments": "0.0.30",
"strip-bom": "^3.0.0",
"strip-json-comments": "^2.0.0"
}
},
"node_modules/tsconfig-paths": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz",
@ -6053,6 +6147,15 @@
"strip-bom": "^3.0.0"
}
},
"node_modules/tsconfig/node_modules/strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
@ -6664,7 +6767,7 @@
},
"dependencies": {
"@accord/ion": {
"version": "git+ssh://git@github.com/accord-dot-app/ion.git#c77ed01bbea93eeeff858004a7f6c6287a6d43de",
"version": "git+ssh://git@github.com/accord-dot-app/ion.git#ea5ce8d2e2073e0260476f0ec96c6603fc502fd3",
"from": "@accord/ion@github:accord-dot-app/ion",
"requires": {
"@accord/ion": "github:accord-dot-app/ion",
@ -6812,6 +6915,9 @@
}
}
},
"@accord/types": {
"version": "file:../types"
},
"@babel/helper-validator-identifier": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
@ -7339,6 +7445,18 @@
"@types/node": "*"
}
},
"@types/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=",
"dev": true
},
"@types/strip-json-comments": {
"version": "0.0.30",
"resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz",
"integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==",
"dev": true
},
"@types/superagent": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.10.tgz",
@ -8357,6 +8475,15 @@
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
"dev": true
},
"dynamic-dedupe": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz",
"integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=",
"dev": true,
"requires": {
"xtend": "^4.0.0"
}
},
"ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@ -11150,7 +11277,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"optional": true
"devOptional": true
},
"strip-json-comments": {
"version": "3.1.1",
@ -11384,6 +11511,12 @@
"punycode": "^2.1.1"
}
},
"tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"dev": true
},
"triple-beam": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
@ -11446,6 +11579,55 @@
"yn": "3.1.1"
}
},
"ts-node-dev": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.8.tgz",
"integrity": "sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg==",
"dev": true,
"requires": {
"chokidar": "^3.5.1",
"dynamic-dedupe": "^0.3.0",
"minimist": "^1.2.5",
"mkdirp": "^1.0.4",
"resolve": "^1.0.0",
"rimraf": "^2.6.1",
"source-map-support": "^0.5.12",
"tree-kill": "^1.2.2",
"ts-node": "^9.0.0",
"tsconfig": "^7.0.0"
},
"dependencies": {
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
}
}
},
"tsconfig": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz",
"integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==",
"dev": true,
"requires": {
"@types/strip-bom": "^3.0.0",
"@types/strip-json-comments": "0.0.30",
"strip-bom": "^3.0.0",
"strip-json-comments": "^2.0.0"
},
"dependencies": {
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true
}
}
},
"tsconfig-paths": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz",

View File

@ -4,15 +4,16 @@
"description": "",
"main": "src/app.ts",
"scripts": {
"start": "ts-node src/app.ts",
"dev": "ts-node-dev --transpile-only src/app.ts",
"debug": "nodemon --exec 'node --inspect=0.0.0.0:9229 --require ts-node/register src/app.ts' --ext 'ts,yml'",
"test": "ts-mocha --exit test/test.ts",
"test:unit": "ts-mocha test/**/**.test.ts"
"start": "npm run start:dev",
"start:dev": "ts-node-dev --transpile-only src/app.ts",
"start:debug": "nodemon --exec 'node --inspect=0.0.0.0:9229 --require ts-node/register src/app.ts' --ext 'ts,yml'",
"start:prod": "ts-node-transpile-only src/app.ts",
"test": "npm run test:int test:unit",
"test:int": "ts-mocha test/int/test.ts",
"test:unit": "ts-mocha --exit test/**/**.test.ts"
},
"keywords": [],
"author": "ADAMJR",
"license": "ISC",
"dependencies": {
"@accord/ion": "github:accord-dot-app/ion",
"body-parser": "^1.19.0",
@ -80,6 +81,7 @@
"mocha": "^8.2.1",
"nodemon": "^2.0.14",
"supertest": "^6.1.3",
"ts-mocha": "^8.0.0"
"ts-mocha": "^8.0.0",
"ts-node-dev": "^1.1.8"
}
}

View File

@ -14,4 +14,4 @@ connect(process.env.MONGO_URI, {
}, (error) => (error)
? log.error(error.message, { uri: process.env.MONGO_URI })
: log.info('Connected to database.', { uri: process.env.MONGO_URI })
)
)

View File

@ -1,7 +1,5 @@
import { APIError } from '../rest/modules/api-error';
import { WS } from '../types/ws';
import DBWrapper from './db-wrapper';
import { Guild } from './models/guild';
import { Invite, InviteDocument } from './models/invite';
import generateInvite from './utils/generate-invite';

View File

@ -1,4 +1,5 @@
import cluster from 'cluster';
import patterns from '../types/patterns';
let inc = 0;
let lastSnowflake: string;
@ -36,6 +37,9 @@ function binary64(val: string) {
// what this method does
// -> https://discord.com/developers/docs/reference#convert-snowflake-to-datetime
export function snowflakeToDate(snowflake: string) {
if (!patterns.snowflake.test(snowflake))
throw new TypeError('Invalid snowflake provided');
const sinceEpochMs = Number(
binary64(snowflake).slice(0, 42 + 2)
);

View File

@ -1,7 +1,5 @@
import { Router } from 'express';
import { SelfUserDocument } from '../../data/models/user';
import { WS } from '../../types/ws';
import { APIError } from '../modules/api-error';
import updateUser from '../middleware/update-user';
import validateUser from '../middleware/validate-user';

View File

@ -1,5 +1,3 @@
import { WS } from '../../types/ws';
export class WSCooldowns {
public readonly active = new Map<string, EventLog[]>();

View File

@ -3,10 +3,7 @@ import { Server as SocketServer } from 'socket.io';
import { WSEvent } from './ws-events/ws-event';
import { resolve } from 'path';
import { readdirSync } from 'fs';
import { WSCooldowns } from './modules/ws-cooldowns';
import { SessionManager } from './modules/session-manager';
import { WS } from '../types/ws';
export class WebSocket {
public events = new Map<keyof WS.To, WSEvent<keyof WS.To>>();

View File

@ -1,8 +1,6 @@
import { Socket } from 'socket.io';
import Channels from '../../data/channels';
import { Guild } from '../../data/models/guild';
import { WS } from '../../types/ws';
import { WSGuard } from '../modules/ws-guard';
import { WebSocket } from '../websocket';
import { WSEvent } from './ws-event';

View File

@ -1,7 +1,6 @@
import { Socket } from 'socket.io';
import { WebSocket } from '../websocket';
import { WSEvent, } from './ws-event';
import { WS } from '../../types/ws';
import { User } from '../../data/models/user';
import { Channel, ChannelDocument } from '../../data/models/channel';
@ -16,13 +15,15 @@ export default class implements WSEvent<'CHANNEL_DELETE'> {
{ voice: { channelId } },
{ voice: {} },
);
ws.io.sockets.in(channelId).socketsLeave(channelId);
ws.io.sockets
.in(channelId)
.socketsLeave(channelId);
await channel.deleteOne();
await this.lowerHigherChannels(channel);
return [{
emit: 'CHANNEL_DELETE',
emit: this.on,
to: [channel.guildId],
send: { channelId, guildId: channel.guildId },
}];

View File

@ -1,4 +1,3 @@
import { WS } from '../../types/ws';
import { WSEvent } from './ws-event';
import { WebSocket } from '../websocket';
import { Socket } from 'socket.io';

View File

@ -1,12 +1,6 @@
import Channels from '../../data/channels';
import { WS } from '../../types/ws';
import { WSGuard } from '../modules/ws-guard';
import { WSEvent } from './ws-event';
import { WebSocket } from '../websocket';
import { Socket } from 'socket.io';
import { VoiceService } from '../../voice/voice-service';
import Users from '../../data/users';
import { SelfUserDocument } from '../../data/models/user';
import { ChannelDocument } from '../../data/models/channel';

View File

@ -2,7 +2,6 @@ import { Socket } from 'socket.io';
import { WebSocket } from '../websocket';
import { WSEvent, } from './ws-event';
import { Channel, ChannelDocument } from '../../data/models/channel';
import { WS } from '../../types/ws';
export default class implements WSEvent<'CHANNEL_UPDATE'> {
on = 'CHANNEL_UPDATE' as const;

View File

@ -1,28 +1,18 @@
import { Socket } from 'socket.io';
import Channels from '../../data/channels';
import { SelfUserDocument, UserDocument } from '../../data/models/user';
import Users from '../../data/users';
import { WS } from '../../types/ws';
import { WebSocket } from '../websocket';
import ChannelLeave from './channel-leave';
import { WSEvent } from './ws-event';
export default class implements WSEvent<'disconnect'> {
on = 'disconnect' as const;
constructor(
private channelLeaveEvent = deps.channelLeave,
private users = deps.users,
) {}
public async invoke(ws: WebSocket, client: Socket) {
const userId = ws.sessions.get(client.id);
const user = await this.users.getSelf(userId);
const user = await deps.users.getSelf(userId);
try {
await this.channelLeaveEvent.invoke(ws, client);
await deps.channelLeaveEvent.invoke(ws, client);
} catch {}
ws.sessions.delete(client.id);

View File

@ -1,5 +1,5 @@
import { Socket } from 'socket.io';
import { WS } from '../../types/ws';
import { WebSocket } from '../websocket';
import { WSEvent } from './ws-event';

View File

@ -6,7 +6,7 @@ import { Invite } from '../../data/models/invite';
import { Message } from '../../data/models/message';
import { Role } from '../../data/models/role';
import { User } from '../../data/models/user';
import { WS } from '../../types/ws';
import { WSGuard } from '../modules/ws-guard';
import { WebSocket } from '../websocket';

View File

@ -2,7 +2,7 @@ import { Socket } from 'socket.io';
import { GuildDocument } from '../../data/models/guild';
import { InviteDocument } from '../../data/models/invite';
import { SelfUserDocument } from '../../data/models/user';
import { WS } from '../../types/ws';
import { WebSocket } from '../websocket';
import { WSEvent, } from './ws-event';

View File

@ -6,7 +6,7 @@ import { GuildMember } from '../../data/models/guild-member';
import { SelfUserDocument, User } from '../../data/models/user';
import Users from '../../data/users';
import { PermissionTypes } from '../../types/permission-types';
import { WS } from '../../types/ws';
import { WSGuard } from '../modules/ws-guard';
import { WebSocket } from '../websocket';

View File

@ -2,7 +2,7 @@ import { Socket } from 'socket.io';
import GuildMembers from '../../data/guild-members';
import Guilds from '../../data/guilds';
import Roles from '../../data/roles';
import { WS } from '../../types/ws';
import { WSGuard } from '../modules/ws-guard';
import { WebSocket } from '../websocket';

View File

@ -8,7 +8,7 @@ import { WSGuard } from '../modules/ws-guard';
import { WebSocket } from '../websocket';
import { WSEvent, } from './ws-event';
import Roles from '../../data/roles';
import { WS } from '../../types/ws';
export default class implements WSEvent<'GUILD_ROLE_CREATE'> {
on = 'GUILD_ROLE_CREATE' as const;

View File

@ -6,7 +6,7 @@ import { WSGuard } from '../modules/ws-guard';
import { WebSocket } from '../websocket';
import { WSEvent, } from './ws-event';
import { GuildMember } from '../../data/models/guild-member';
import { WS } from '../../types/ws';
import Roles from '../../data/roles';
export default class implements WSEvent<'GUILD_ROLE_DELETE'> {

View File

@ -3,7 +3,7 @@ import { Socket } from 'socket.io';
import { WSGuard } from '../modules/ws-guard';
import { WebSocket } from '../websocket';
import { WSEvent, } from './ws-event';
import { WS } from '../../types/ws';
import Roles from '../../data/roles';
import Guilds from '../../data/guilds';
import GuildMembers from '../../data/guild-members';

View File

@ -3,7 +3,7 @@ import { Socket } from 'socket.io';
import { WSGuard } from '../modules/ws-guard';
import { WebSocket } from '../websocket';
import { WSEvent, } from './ws-event';
import { WS } from '../../types/ws';
export default class implements WSEvent<'GUILD_UPDATE'> {
on = 'GUILD_UPDATE' as const;

View File

@ -1,7 +1,7 @@
import { Socket } from 'socket.io';
import Invites from '../../data/invites';
import { PermissionTypes } from '../../types/permission-types';
import { WS } from '../../types/ws';
import { WSGuard } from '../modules/ws-guard';
import { WebSocket } from '../websocket';

View File

@ -1,5 +1,5 @@
import { Socket } from 'socket.io';
import { WS } from '../../types/ws';
import { WebSocket } from '../websocket';
import { WSEvent, } from './ws-event';

View File

@ -1,7 +1,7 @@
import { Socket } from 'socket.io';
import { WebSocket } from '../websocket';
import { WSEvent, } from './ws-event';
import { WS } from '../../types/ws';
import { Channel } from '../../data/models/channel';
import striptags from 'striptags';

View File

@ -1,6 +1,6 @@
import { Socket } from 'socket.io';
import { Message } from '../../data/models/message';
import { WS } from '../../types/ws';
import { WebSocket } from '../websocket';
import { WSEvent } from './ws-event';

View File

@ -6,7 +6,7 @@ import got from 'got';
import { WSGuard } from '../modules/ws-guard';
import Messages from '../../data/messages';
import { WS } from '../../types/ws';
export default class implements WSEvent<'MESSAGE_UPDATE'> {
on = 'MESSAGE_UPDATE' as const;

View File

@ -2,7 +2,7 @@ import { Socket } from 'socket.io';
import Channels from '../../data/channels';
import { SelfUserDocument } from '../../data/models/user';
import Users from '../../data/users';
import { WS } from '../../types/ws';
import { WSGuard } from '../modules/ws-guard';
import { WSRooms } from '../modules/ws-rooms';

View File

@ -1,5 +1,5 @@
import { Socket } from 'socket.io';
import { WS } from '../../types/ws';
import { WebSocket } from '../websocket';
import { WSEvent, } from './ws-event';

View File

@ -1,7 +1,7 @@
import { WSEvent } from './ws-event';
import { Socket } from 'socket.io';
import { WebSocket } from '../websocket';
import { WS } from '../../types/ws';
import generateInvite from '../../data/utils/generate-invite';
export default class implements WSEvent<'USER_DELETE'> {

View File

@ -1,7 +1,7 @@
import { Socket } from 'socket.io';
import Users from '../../data/users';
import { EmailFunctions } from '../../email/email-functions';
import { WS } from '../../types/ws';
import { WSGuard } from '../modules/ws-guard';
import { WebSocket } from '../websocket';

View File

@ -1,5 +1,5 @@
import { Socket } from 'socket.io';
import { WS } from '../../types/ws';
import { VoiceService } from '../../voice/voice-service';
import { WebSocket } from '../websocket';

View File

@ -1,5 +1,5 @@
import { Socket } from 'socket.io';
import { WS } from '../../types/ws';
import { WebSocket } from '../websocket';
type OnWS = WS.To & WS.On;

View File

@ -1,8 +0,0 @@
# Special .env for integration testing. Rename to '.env'.
API_URL="http://localhost:3001/api"
EMAIL_ADDRESS="example@gmail.com"
EMAIL_PASSWORD="google_account_password"
MONGO_URI="mongodb://localhost/accord-test"
PORT=3001
ROOT_ENDPOINT="http://localhost:3001"
WEBSITE_URL="http://localhost:4200"

View File

@ -22,16 +22,6 @@ use(should);
useFindAndModify: false,
useCreateIndex: true,
});
try {
execSync(`kill -9 $(lsof -i :${process.env.PORT} | tail -n 1 | cut -d ' ' -f5) 2>> /dev/null`);
} catch {}
await import('./ws/channel-delete.tests');
})();
/**
* e2e: testing the final product (i.e. app)
* integration: testing full unit with dependencies
* unit: testing one unit (i.e. one class, function etc.) - mocks dependencies
*/
import('./ws/channel-delete.test');

View File

@ -1,18 +1,15 @@
import ChannelDelete from '../../../src/ws/ws-events/channel-delete';
import { given, test } from '@accord/ion';
import { WebSocket } from '../../../src/ws/websocket';
import { WS } from '../../../src/types/ws';
test(channelDelete, () => {
before(() => console.log('before'));
given({}).rejectWith('Channel not found');
after(() => console.log('after'));
given({}).rejectWith('Channel not found');
});
async function channelDelete(args: WS.To['CHANNEL_DELETE']) {
await import('../../../src/modules/deps');
console.error = () => {};
global['log'] = console;
const event = new ChannelDelete();

View File

@ -15,7 +15,7 @@ describe('snowflake-entity', () => {
});
test(snowflakeToDate, () => {
given(generateSnowflake())
.assert(d => d.toString() === new Date().toString());
given(generateSnowflake()).assert(d => d.toString() === new Date().toString());
given('invalid snowflake').throw('Invalid snowflake provided');
});
});

22
docker-compose.dev.yml Normal file
View File

@ -0,0 +1,22 @@
version: '3.8'
services:
database:
image: mongo:4.4-focal
ports: [27018:27017]
volumes: [accord:/data/db]
types:
build: ./types
backend:
depends_on: [database]
build: ./backend
ports: [3000:3000]
env_file: [./backend/.env]
volumes: [./backend:/app]
frontend:
depends_on: [database]
build: ./frontend
ports: [4200:4200]
env_file: [./frontend/env/.env.dev]
volumes: [./frontend:/app]
volumes:
accord:

16
docker-compose.prod.yml Normal file
View File

@ -0,0 +1,16 @@
version: '3.8'
services:
database:
image: mongo:4.4-focal
ports: [27017:27017]
volumes: [accord:/data/db]
types:
build: ./types
backend:
depends_on: [database]
build: ./backend
ports: [3000:3000]
env_file: [./backend/.env]
volumes: [./backend:/app]
volumes:
accord:

1
frontend/.dockerignore Symbolic link
View File

@ -0,0 +1 @@
../.gitignore

13
frontend/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
FROM node:16-alpine3.14
RUN addgroup app && adduser -SG app app
WORKDIR /app
COPY --chown=app:app package*.json ./
RUN npm i
COPY . .
RUN unlink /app/src/types
COPY --chown=app:app --from=accord_types:latest /app /app/src/types
EXPOSE 4200
USER app
CMD npm run start:dev

5
frontend/env/.env.dev vendored Normal file
View File

@ -0,0 +1,5 @@
PORT=4200
REACT_APP_API_URL="backend/v2"
REACT_APP_CDN_URL="backend/assets"
REACT_APP_REPO="https://github.com/accord-dot-app/app"
REACT_APP_ROOT_API_URL="backend"

View File

@ -48,10 +48,11 @@
"web-vitals": "^1.1.2"
},
"scripts": {
"start": "dotenv -e src/env/.env.dev craco start",
"start:prod": "dotenv -e src/env/.env.prod craco start",
"build:dev": "dotenv -e src/env/.env.dev craco build",
"build:prod": "dotenv -e src/env/.env.prod craco build",
"start": "npm run start:dev",
"start:dev": "craco start",
"start:prod": "craco start",
"build:dev": "dotenv -e env/.env.dev craco build",
"build:prod": "dotenv -e env/.env.prod craco build",
"test": "craco test",
"test:unit": "craco test --watchAll=false",
"test:e2e": "cypress open",

View File

@ -1,5 +0,0 @@
PORT=4200
REACT_APP_API_URL="http://localhost:3000/v2"
REACT_APP_CDN_URL="http://localhost:3000/assets"
REACT_APP_REPO="https://github.com/accord-dot-app/app"
REACT_APP_ROOT_API_URL="http://localhost:3000"

View File

@ -1,5 +1,4 @@
import io from 'socket.io-client';
import { WS } from '../types/ws';
const ws = (io as any).connect(process.env.REACT_APP_ROOT_API_URL, {
secure: true,

View File

@ -1,5 +1,4 @@
import { createAction } from '@reduxjs/toolkit';
import { WS } from '../types/ws';
import { headers } from './utils/rest-headers';
export const actions = {

View File

@ -1,5 +1,4 @@
import { createSlice } from '@reduxjs/toolkit';
import { WS } from '../types/ws';
import { actions as api } from './api';
import { openDialog } from './ui';
import { headers, token } from './utils/rest-headers';

View File

@ -1,5 +1,4 @@
import { createSelector, createSlice } from '@reduxjs/toolkit';
import { WS } from '../types/ws';
import { actions as api } from './api';
import { notInArray } from './utils/filter';

View File

@ -1,6 +1,5 @@
import { createSelector, createSlice } from '@reduxjs/toolkit';
import { byAscending } from '../components/utils/vanilla/sort';
import { WS } from '../types/ws';
import { actions as api, uploadFile } from './api';
import { notInArray } from './utils/filter';

View File

@ -1,5 +1,4 @@
import { createSlice } from '@reduxjs/toolkit';
import { WS } from '../types/ws';
import { actions as api } from './api';
import { notInArray } from './utils/filter';

View File

@ -1,5 +1,4 @@
import { createSelector, createSlice } from '@reduxjs/toolkit';
import { WS } from '../types/ws';
import { actions as api } from './api';
import { notInArray } from './utils/filter';

View File

@ -1,5 +1,4 @@
import { createSlice, createSelector } from '@reduxjs/toolkit';
import { WS } from '../types/ws';
import { actions as api, uploadFile } from './api';
import { notInArray } from './utils/filter';
import { headers } from './utils/rest-headers';

View File

@ -1,7 +1,6 @@
import { createSelector, createSlice } from '@reduxjs/toolkit';
import { notInArray } from './utils/filter';
import { actions as api } from './api';
import { WS } from '../types/ws';
import { byMax } from './utils/reduce';
const slice = createSlice({

View File

@ -1,5 +1,4 @@
import { createSlice, createSelector } from '@reduxjs/toolkit';
import { WS } from '../types/ws';
import { actions as api, uploadFile } from './api';
import { actions as meta } from './meta';
import { notInArray } from './utils/filter';

10
package.json Normal file
View File

@ -0,0 +1,10 @@
{
"scripts": {
"dc:up": "sudo docker-compose -f docker-compose.dev.yml up -d",
"dc:down": "sudo docker-compose -f docker-compose.dev.yml down",
"dc:ps": "sudo docker-compose -f docker-compose.dev.yml ps",
"dc:build": "sudo docker-compose -f docker-compose.prod.yml build",
"log:f": "sudo docker-compose -f docker-compose.dev.yml logs backend",
"log:b": "sudo docker-compose -f docker-compose.dev.yml logs frontend"
}
}

4
types/Dockerfile Normal file
View File

@ -0,0 +1,4 @@
FROM node:16-alpine3.14
WORKDIR /app
COPY . .

10
types/ws.d.ts vendored
View File

@ -376,14 +376,4 @@ declare namespace WS {
voice: UserTypes.VoiceState;
}
}
}
/** @deprecated In process of being replaced with Partial. */
// FIXME: Removing this breaks react.
export namespace PartialEntity {
export type Guild = Partial<Entity.Guild>;
export type GuildMember = Partial<Entity.GuildMember>;
export type Message = Partial<Entity.Message>;
export type Role = Partial<Entity.Role>;
export type User = Partial<UserTypes.Self>;
}