channel list
parent
0159cfde99
commit
bf6d521963
|
|
@ -56,3 +56,6 @@ thumbs.db
|
||||||
# Editor-based Rest Client
|
# Editor-based Rest Client
|
||||||
.idea/httpRequests
|
.idea/httpRequests
|
||||||
/.idea/csv-plugin.xml
|
/.idea/csv-plugin.xml
|
||||||
|
|
||||||
|
# docker data
|
||||||
|
docker-volume
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,16 @@ services:
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
MARIADB_ROOT_PASSWORD: example
|
MARIADB_ROOT_PASSWORD: example
|
||||||
MARIADB_ROOT_HOST: '127.0.0.1'
|
MARIADB_DATABASE: corner
|
||||||
|
MARIADB_USER: corner
|
||||||
|
MARIADB_PASSWORD: corner
|
||||||
ports:
|
ports:
|
||||||
- 3306:3306
|
- 3306:3306
|
||||||
|
# volumes:
|
||||||
|
# - ./docker-volume:/var/lib/mysql
|
||||||
|
|
||||||
adminer:
|
adminer:
|
||||||
image: adminer
|
image: adminer
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-unused-vars": "off",
|
"no-unused-vars": "off",
|
||||||
"@typescript-eslint/no-unused-vars": "off",
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "off"
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/ban-types": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<title>Vite App</title>
|
<title>Vite App</title>
|
||||||
</head>
|
</head>
|
||||||
<body style="margin: 0px; overflow: hidden; color: #f8f8f2; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif">
|
<body style="margin: 0px; overflow: hidden; color: #f8f8f2; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif">
|
||||||
<div id="app" style="width: 100vw; height: 100vh"></div>
|
<div id="app" style="width: 100vw; height: 100vh; background: #282a36"></div>
|
||||||
<script src="./src/index.tsx" type="module"></script>
|
<script src="./src/index.tsx" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
import Channels from './pages/Channels';
|
||||||
import Chat from './pages/Chat';
|
import Chat from './pages/Chat';
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
(
|
(
|
||||||
<>
|
<>
|
||||||
<Chat
|
{/* <Chat
|
||||||
|
|
||||||
></Chat>
|
></Chat> */}
|
||||||
|
<Channels
|
||||||
|
|
||||||
|
></Channels>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
document.getElementById('app'),
|
document.getElementById('app'),
|
||||||
|
|
|
||||||
|
|
@ -27,21 +27,18 @@ const connect = async () => {
|
||||||
if(socket === null) return;
|
if(socket === null) return;
|
||||||
connectionAttempts = 0;
|
connectionAttempts = 0;
|
||||||
// socket.send('Hello Server!');
|
// socket.send('Hello Server!');
|
||||||
console.log('API Connected');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addEventListener('message', (event) => {
|
socket.addEventListener('message', (event) => {
|
||||||
console.log('API Broadcasted', event.data);
|
|
||||||
const {action, data} = JSON.parse(event.data);
|
const {action, data} = JSON.parse(event.data);
|
||||||
|
console.log('[IN]', action, data);
|
||||||
for(const router of routers) {
|
for(const router of routers) {
|
||||||
// debugger;
|
|
||||||
router(action, data);
|
router(action, data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addEventListener('close', () => {
|
socket.addEventListener('close', () => {
|
||||||
socket = null;
|
socket = null;
|
||||||
console.log('API Closed');
|
|
||||||
connect();
|
connect();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { registerRouter, router, send, unregisterRouter } from '../lib/api';
|
||||||
|
|
||||||
|
function useRouter(actions: Function | object, deps: any[]) {
|
||||||
|
const _router = typeof actions === 'object' ? router(actions) : actions;
|
||||||
|
useEffect(() => {
|
||||||
|
registerRouter(_router);
|
||||||
|
return () => {
|
||||||
|
unregisterRouter(_router);
|
||||||
|
};
|
||||||
|
}, deps);
|
||||||
|
|
||||||
|
return {
|
||||||
|
send: send,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IChannel {
|
||||||
|
uid: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Channels() {
|
||||||
|
|
||||||
|
const [channels, setChannels] = useState<IChannel[]>([]);
|
||||||
|
|
||||||
|
const { send } = useRouter({
|
||||||
|
'channels:list'(data: any) {
|
||||||
|
// console.log(data)
|
||||||
|
setChannels(data);
|
||||||
|
},
|
||||||
|
}, [channels]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(channels.length === 0) {
|
||||||
|
send('channels:list');
|
||||||
|
}
|
||||||
|
}, [channels]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{channels.map(channel => (
|
||||||
|
<div key={channel.uid}>
|
||||||
|
<span style={{
|
||||||
|
fontWeight: 'bold',
|
||||||
|
}}>#</span>{channel.name}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -60,7 +60,6 @@ export default () => {
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
background: '#282a36',
|
|
||||||
height: '100%',
|
height: '100%',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
DB_HOST=localhost
|
DB_HOST=localhost
|
||||||
DB_USER=root
|
DB_USER=corner
|
||||||
DB_PASSWORD=example
|
DB_PASSWORD=corner
|
||||||
DB_DB=corner
|
DB_DB=corner
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-unused-vars": "off",
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "off"
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/ban-types": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,17 +65,17 @@ export async function update() {
|
||||||
// determine version
|
// determine version
|
||||||
const currentVersion: number = await new Promise((resolve, rej) => {
|
const currentVersion: number = await new Promise((resolve, rej) => {
|
||||||
migrationConnection.query(`
|
migrationConnection.query(`
|
||||||
SELECT SCHEMA_NAME
|
SELECT COUNT(*) as tables
|
||||||
FROM INFORMATION_SCHEMA.SCHEMATA
|
FROM information_schema.tables
|
||||||
WHERE SCHEMA_NAME = '${database}';
|
WHERE table_schema = '${database}';
|
||||||
`, async (err, res, fields) => {
|
`, async (err, res, fields) => {
|
||||||
if(res.length === 0) {
|
if(res[0].tables === 0) {
|
||||||
await new Promise((resolve, reject) => {
|
// await new Promise((resolve, reject) => {
|
||||||
migrationConnection.query(`CREATE DATABASE \`${database}\` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;`, (err, res) => {
|
// migrationConnection.query(`CREATE DATABASE \`${database}\` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;`, (err, res) => {
|
||||||
if(err) return reject(err);
|
// if(err) return reject(err);
|
||||||
return resolve(res);
|
// return resolve(res);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
resolve(0);
|
resolve(0);
|
||||||
} else {
|
} else {
|
||||||
const version: number = await new Promise((resolve, reject) => {
|
const version: number = await new Promise((resolve, reject) => {
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
SELECT (name, uid) FROM channels;
|
SELECT name, uid FROM channels;
|
||||||
|
|
@ -1,54 +1,23 @@
|
||||||
import { WebSocketServer } from 'ws';
|
import router from './lib/router';
|
||||||
import router from './routers/root';
|
import message from './routers/message';
|
||||||
|
import { expose } from './lib/WebSocketServer';
|
||||||
|
|
||||||
const wss = new WebSocketServer({
|
const api = router({
|
||||||
port: 3000,
|
up() {
|
||||||
}, () => {
|
console.log(Date.now());
|
||||||
console.log('ws chat server started on dev.valnet.xyz');
|
},
|
||||||
|
message: message,
|
||||||
|
messages: message,
|
||||||
|
channel: channel,
|
||||||
|
channels: channel,
|
||||||
});
|
});
|
||||||
|
|
||||||
wss.on('connection', (ws) => {
|
expose(api, 3000);
|
||||||
ws.on('message', async (str) => {
|
|
||||||
try {
|
|
||||||
const message = JSON.parse(str.toString());
|
|
||||||
if(typeof message.action !== 'string') {
|
|
||||||
console.warn('invalid JSON message');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const {action, data} = message;
|
|
||||||
try {
|
|
||||||
console.log(action, data);
|
|
||||||
const _return = await (router(action, data) as unknown as Promise<any>);
|
|
||||||
console.log(_return);
|
|
||||||
if(_return) {
|
|
||||||
ws.send(JSON.stringify(_return));
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
console.warn(`error in action ${action}`);
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('JSON parse failed on message');
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
export function send(client: any, action: string, data?: any) {
|
|
||||||
client.send(JSON.stringify({action, data}));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function broadcast(action: string, data?: any) {
|
|
||||||
for(const client of wss.clients) {
|
|
||||||
send(client, action, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default wss;
|
|
||||||
|
|
||||||
// -------------
|
// -------------
|
||||||
|
|
||||||
import { update } from './db/migrate';
|
import { update } from './db/migrate';
|
||||||
|
import channel from './routers/channel';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
update();
|
update();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { WebSocketServer } from 'ws';
|
||||||
|
|
||||||
|
export function expose(router: Function, port: number) {
|
||||||
|
const wss = new WebSocketServer({
|
||||||
|
port: 3000,
|
||||||
|
}, () => {
|
||||||
|
console.log('ws chat server started on dev.valnet.xyz');
|
||||||
|
});
|
||||||
|
|
||||||
|
wss.on('connection', (ws) => {
|
||||||
|
ws.on('message', async (str) => {
|
||||||
|
try {
|
||||||
|
const message = JSON.parse(str.toString());
|
||||||
|
if(typeof message.action !== 'string') {
|
||||||
|
console.warn('invalid JSON message');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {action, data} = message;
|
||||||
|
try {
|
||||||
|
console.log('[IN]', action, data);
|
||||||
|
const _return = await (router(action, data) as unknown as Promise<any>);
|
||||||
|
console.log(_return);
|
||||||
|
if(_return) {
|
||||||
|
try {
|
||||||
|
switch(_return.type) {
|
||||||
|
case ResponseType.BROADCAST: {
|
||||||
|
console.log('[OUT_BROADCAST]', action, _return.data);
|
||||||
|
for(const client of wss.clients) {
|
||||||
|
send(client, action, _return.data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ResponseType.REPLY: {
|
||||||
|
console.log('[OUT]', action, _return.data);
|
||||||
|
send(ws, action, _return.data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
console.warn(`error in action ${action}`);
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('JSON parse failed on message');
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ResponseType {
|
||||||
|
BROADCAST,
|
||||||
|
REPLY
|
||||||
|
}
|
||||||
|
|
||||||
|
function send(client: any, action: string, data?: any) {
|
||||||
|
client.send(JSON.stringify({action, data}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function broadcast(data: any) {
|
||||||
|
return {
|
||||||
|
type: ResponseType.BROADCAST,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reply(data: any) {
|
||||||
|
return {
|
||||||
|
type: ResponseType.REPLY,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// export default wss;
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
export default function router(routes: any) {
|
|
||||||
|
|
||||||
|
|
||||||
|
export default function router(routes: any) {
|
||||||
for(const routeName in routes) {
|
for(const routeName in routes) {
|
||||||
const route = routes[routeName];
|
const route = routes[routeName];
|
||||||
if('routes' in route) {
|
if('routes' in route) {
|
||||||
|
|
@ -12,7 +13,6 @@ export default function router(routes: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendFn = function(route: any, data: any) {
|
const sendFn = function(route: any, data: any) {
|
||||||
console.log(routes);
|
|
||||||
if(route in routes) {
|
if(route in routes) {
|
||||||
return routes[route](data);
|
return routes[route](data);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import query from '../db/query';
|
||||||
|
import router from '../lib/router';
|
||||||
|
import list from '../db/snippets/channel/list.sql';
|
||||||
|
import { reply } from '../lib/WebSocketServer';
|
||||||
|
|
||||||
|
export default router({
|
||||||
|
async list() {
|
||||||
|
const res = await query(list);
|
||||||
|
return reply(res ?? undefined);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -1,29 +1,26 @@
|
||||||
import { broadcast } from '..';
|
|
||||||
import query from '../db/query';
|
import query from '../db/query';
|
||||||
import router from '../router';
|
import router from '../lib/router';
|
||||||
import newMessage from '../db/snippets/message/new.sql';
|
import newMessage from '../db/snippets/message/new.sql';
|
||||||
import recentMessages from '../db/snippets/message/recent.sql';
|
import recentMessages from '../db/snippets/message/recent.sql';
|
||||||
|
import { broadcast, reply } from '../lib/WebSocketServer';
|
||||||
|
|
||||||
export default router({
|
export default router({
|
||||||
async message(data: any) {
|
async message(data: any) {
|
||||||
const failed = null === await query(newMessage, data.text, data.from, data.uid, data.timestamp);
|
const failed = null === await query(newMessage, data.text, data.from, data.uid, data.timestamp);
|
||||||
if(failed) return;
|
if(failed) return;
|
||||||
broadcast('message:message', data);
|
return broadcast(data);
|
||||||
},
|
},
|
||||||
async recent() {
|
async recent() {
|
||||||
console.log('got recents request');
|
console.log('got recents request');
|
||||||
const messages = await query(recentMessages);
|
const messages = await query(recentMessages);
|
||||||
if(messages === null) return;
|
if(messages === null) return;
|
||||||
return {
|
return reply({
|
||||||
action: 'message:recent',
|
messages: messages.map(v => ({
|
||||||
data: {
|
from: v.from,
|
||||||
messages: messages.map(v => ({
|
uid: v.uid,
|
||||||
from: v.from,
|
timestamp: v.t_sent * 1000,
|
||||||
uid: v.uid,
|
text: v.text,
|
||||||
timestamp: v.t_sent * 1000,
|
})),
|
||||||
text: v.text,
|
});
|
||||||
})),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import router from '../router';
|
|
||||||
import message from './message';
|
|
||||||
|
|
||||||
export default router({
|
|
||||||
up() {
|
|
||||||
console.log(Date.now());
|
|
||||||
},
|
|
||||||
message: message,
|
|
||||||
});
|
|
||||||
Reference in New Issue