Skip to content

Commit 57e7d3f

Browse files
author
Moritz Roessler
committed
fix: add route support
1 parent 81f39ba commit 57e7d3f

13 files changed

+178
-20
lines changed

.eslintrc.js

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ module.exports = {
1818
},
1919
plugins: ['@typescript-eslint', 'prettier'],
2020
rules: {
21+
'no-underscore-dangle': 'off',
22+
'no-restricted-syntax': 'off',
2123
'no-unused-expressions': 'off',
2224
'linebreak-style': 'off',
2325
'import/extensions': [

dist/components/Lists.js

+26-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ var _uuid = require("uuid");
1414
var _ServerSideProps = require("./ServerSideProps");
1515
var _config = require("../config");
1616
var _jsonwebtoken = _interopRequireDefault(require("jsonwebtoken"));
17+
var _ExpressServer = require("./ExpressServer");
18+
var _instances = require("../instances");
1719
var _jsxRuntime = require("@state-less/react-server/dist/jsxRenderer/jsx-runtime");
1820
var _excluded = ["order", "points", "iat"];
1921
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
@@ -131,6 +133,7 @@ var Todo = function Todo(_ref3, _ref4) {
131133
color: color
132134
}));
133135
};
136+
console.log('Render todo', todo);
134137
var toggle = function toggle() {
135138
var _user3;
136139
var store = _reactServer.Dispatcher.getCurrent().getStore();
@@ -147,6 +150,7 @@ var Todo = function Todo(_ref3, _ref4) {
147150
lastModified: Date.now(),
148151
creditedValuePoints: comp ? 0 : valuePoints
149152
});
153+
console.log('toggling', todo, newTodo);
150154
setTodo(newTodo);
151155
var newItems = !comp ? [].concat((0, _toConsumableArray2["default"])(lastCompleted.value[valuePoints] || []), [newTodo]) : (lastCompleted.value[valuePoints] || []).filter(function (i) {
152156
return i.id !== todo.id;
@@ -157,6 +161,7 @@ var Todo = function Todo(_ref3, _ref4) {
157161
});
158162
lastCompleted.value = _objectSpread(_objectSpread({}, lastCompleted.value || {}), {}, (0, _defineProperty2["default"])({}, valuePoints, filtered));
159163
points.value += comp ? -todo.creditedValuePoints : valuePoints;
164+
return newTodo;
160165
};
161166
var archive = function archive() {
162167
if (todo.archived) return;
@@ -225,7 +230,27 @@ var Todo = function Todo(_ref3, _ref4) {
225230
setDueTime: setDueTime,
226231
type: "Todo",
227232
createdAt: createdAt,
228-
lastModified: todo.lastModified
233+
lastModified: todo.lastModified,
234+
children: (0, _jsxRuntime.jsx)(_ExpressServer.Route, {
235+
todo: todo,
236+
app: _instances.app,
237+
path: "/todos/".concat(id, "/toggle"),
238+
get: function get(req, res) {
239+
console.log('Hello from HTTP');
240+
var todo = toggle();
241+
res.send(todo);
242+
},
243+
authenticate: function authenticate(req, res, next) {
244+
console.log('Hello from HTTP');
245+
// Authenticate the http request
246+
(0, _reactServer.authenticate)(req.headers, _config.JWT_SECRET);
247+
// Make sure the client is the same
248+
if (req.headers['x-unique-id'] !== clientId) {
249+
throw new Error('Unauthorized');
250+
}
251+
next();
252+
}
253+
}, "test")
229254
}), (0, _reactServer.clientKey)("".concat(id, "-todo"), context));
230255
};
231256
exports.Todo = Todo;

dist/index.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ var _apolloServerExpress = require("apollo-server-express");
1212
var _graphqlTools = require("graphql-tools");
1313
var _graphql = require("graphql");
1414
var _http = require("http");
15-
var _express = _interopRequireDefault(require("express"));
1615
var _subscriptionsTransportWs = require("subscriptions-transport-ws");
1716
var _reactServer = require("@state-less/react-server");
1817
var _instances = require("./instances");
@@ -37,7 +36,6 @@ var _jsxRuntime = require("@state-less/react-server/dist/jsxRenderer/jsx-runtime
3736
var _templateObject, _templateObject2;
3837
_reactServer.Dispatcher.getCurrent().setStore(_instances.store);
3938
_reactServer.Dispatcher.getCurrent().setPubSub(_instances.pubsub);
40-
var app = (0, _express["default"])();
4139
var PORT = 4000;
4240
var schema = (0, _graphqlTools.makeExecutableSchema)({
4341
typeDefs: _schema.typeDefs,
@@ -54,7 +52,7 @@ var apolloServer = new _apolloServerExpress.ApolloServer({
5452
}
5553
});
5654
// Create a HTTP server
57-
var httpServer = (0, _http.createServer)(app);
55+
var httpServer = (0, _http.createServer)(_instances.app);
5856
var connections = _instances.store.createState(0, {
5957
key: 'connections',
6058
scope: 'global'
@@ -114,7 +112,7 @@ var node = (0, _reactServer.render)(reactServer, null, null);
114112
return apolloServer.start();
115113
case 2:
116114
apolloServer.applyMiddleware({
117-
app: app,
115+
app: _instances.app,
118116
bodyParserConfig: {
119117
limit: '10mb'
120118
}

dist/instances.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
44
Object.defineProperty(exports, "__esModule", {
55
value: true
66
});
7-
exports.store = exports.pubsub = exports.notificationEngine = void 0;
7+
exports.store = exports.pubsub = exports.notificationEngine = exports.app = void 0;
88
var _graphqlSubscriptions = require("graphql-subscriptions");
99
var _reactServer = require("@state-less/react-server");
10+
var _express = _interopRequireDefault(require("express"));
11+
var _cors = _interopRequireDefault(require("cors"));
1012
var _logger = _interopRequireDefault(require("./lib/logger"));
1113
var _NotificationEngine = require("./lib/NotificationEngine");
1214
var pubsub = new _graphqlSubscriptions.PubSub();
@@ -24,5 +26,8 @@ var notificationEngine = new _NotificationEngine.NotificationEngine({
2426
logger: _logger["default"]
2527
});
2628
exports.notificationEngine = notificationEngine;
29+
var app = (0, _express["default"])();
30+
exports.app = app;
31+
app.use((0, _cors["default"])());
2732
store.sync(20 * 1000);
2833
notificationEngine.start();

dist/lib/NotificationEngine.js

+8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl
1212
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
1313
var _webPush = _interopRequireDefault(require("web-push"));
1414
var _dateFns = require("date-fns");
15+
var _jwtSimple = _interopRequireDefault(require("jwt-simple"));
16+
var _config = require("../config");
1517
var _templateObject, _templateObject2, _templateObject3, _templateObject4;
1618
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
1719
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
@@ -89,6 +91,7 @@ var NotificationEngine = /*#__PURE__*/function () {
8991
}, {
9092
key: "subscribe",
9193
value: function subscribe(clientId, user, subscription) {
94+
console.log('Subscribe', user);
9295
this._clients.push({
9396
id: clientId,
9497
sub: subscription,
@@ -136,8 +139,13 @@ var NotificationEngine = /*#__PURE__*/function () {
136139
});
137140
Object.assign(todo, stored.value);
138141
console.log('Checking Todo', stored.value.title);
142+
var token = _jwtSimple["default"].encode(user, _config.JWT_SECRET);
139143
if (checkTodo(stored.value, clientId)) {
140144
_this2.sendNotification(sub, {
145+
token: token,
146+
clientId: clientId,
147+
id: stored.value.id,
148+
actions: ['complete'],
141149
title: stored.value.title,
142150
body: "It's almost ".concat((0, _dateFns.format)(new Date(stored.value.dueTime), 'hh:mm'))
143151
});

dist/lib/logger.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers
99
var _l0g = require("l0g");
1010
var _Color = require("l0g/formatters/Color");
1111
var _ConsoleTransport = require("l0g/transports/ConsoleTransport");
12-
var _FileTransport = require("l0g/transports/FileTransport");
1312
var _ReloadConfigFeature = require("l0g/features/ReloadConfigFeature");
1413
var _format = require("./format");
1514
var _require = require('../../package.json'),
@@ -48,10 +47,10 @@ _Color.Color.colors.key.level.notice = 'yellow';
4847
/** Define the used transports */
4948
var transports = [new _ConsoleTransport.ConsoleTransport({
5049
formatter: formatter
51-
}), /** Uncomment the line to output a log file */
52-
new _FileTransport.FileTransport("".concat(packageName, ".log"), {
53-
formatter: _Color.Formatter
54-
})];
50+
})
51+
/** Uncomment the line to output a log file */
52+
// new FileTransport(`${packageName}.log`, { formatter: Formatter }),
53+
];
5554

5655
/** Create the base logger instance. With a default LOG_LEVEL of 'debug' if no environment variable is present */
5756
var logger = new _l0g.Logger('debug', {

src/components/ExpressServer.tsx

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { useClientEffect } from '@state-less/react-server';
2+
import express from 'express';
3+
4+
const latestHandlers = new Map();
5+
const latestMiddleware = new Map();
6+
7+
function handlerProxy(req, res, next) {
8+
const currentPath = req.path;
9+
const handler = latestHandlers.get(currentPath);
10+
if (handler) {
11+
return handler(req, res, next);
12+
}
13+
next(); // Move on if no handler is found
14+
}
15+
16+
function middlewareProxy(req, res, next) {
17+
const currentPath = req.path;
18+
const handler = latestMiddleware.get(currentPath);
19+
if (handler) {
20+
return handler(req, res, next);
21+
}
22+
next(); // Move on if no handler is found
23+
}
24+
function deleteRoute(router, path) {
25+
const ind = router.stack?.findIndex((route) => {
26+
return route.path === path;
27+
});
28+
if (ind !== -1) {
29+
router.stack.splice(ind, 1);
30+
}
31+
}
32+
export const Route = (props, { key, context }) => {
33+
const { app, path, get, authenticate } = props;
34+
35+
useClientEffect(() => {
36+
if ('get' in props) {
37+
deleteRoute(app._router, path);
38+
app.get(path, middlewareProxy, handlerProxy);
39+
40+
// Update the handler in our store
41+
if (authenticate) {
42+
latestMiddleware.set(path, authenticate);
43+
} else {
44+
latestMiddleware.delete(path);
45+
}
46+
47+
latestHandlers.set(path, get);
48+
}
49+
}, [path, get, app]);
50+
};

src/components/Lists.tsx

+25-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { v4 } from 'uuid';
1010
import { ServerSideProps } from './ServerSideProps';
1111
import { JWT_SECRET } from '../config';
1212
import jwt, { decode } from 'jsonwebtoken';
13+
import { Route } from './ExpressServer';
14+
import { app } from '../instances';
1315

1416
const itemTypeStateKeyMap = {
1517
Todo: 'todo',
@@ -220,6 +222,8 @@ export const Todo = (
220222
};
221223

222224
points.value += comp ? -todo.creditedValuePoints : valuePoints;
225+
226+
return newTodo;
223227
};
224228

225229
const archive = () => {
@@ -316,7 +320,27 @@ export const Todo = (
316320
type="Todo"
317321
createdAt={createdAt}
318322
lastModified={todo.lastModified}
319-
/>
323+
>
324+
<Route
325+
todo={todo}
326+
key="test"
327+
app={app}
328+
path={`/todos/${id}/toggle`}
329+
get={(req, res) => {
330+
const todo = toggle();
331+
res.send(todo);
332+
}}
333+
authenticate={(req, res, next) => {
334+
// Authenticate the http request
335+
authenticate(req.headers, JWT_SECRET);
336+
// Make sure the client is the same
337+
if (req.headers['x-unique-id'] !== clientId) {
338+
throw new Error('Unauthorized');
339+
}
340+
next();
341+
}}
342+
/>
343+
</ServerSideProps>
320344
);
321345
};
322346

src/index.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
createContext,
1515
} from '@state-less/react-server';
1616

17-
import { pubsub, store } from './instances';
17+
import { app, pubsub, store } from './instances';
1818

1919
import { generatePubSubKey, resolvers } from './resolvers';
2020
import { typeDefs } from './schema';
@@ -37,7 +37,6 @@ import { WebPushManager } from './components/WebPushManager';
3737
Dispatcher.getCurrent().setStore(store);
3838
Dispatcher.getCurrent().setPubSub(pubsub);
3939

40-
const app = express();
4140
const PORT = 4000;
4241

4342
const schema = makeExecutableSchema({

src/instances.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { PubSub } from 'graphql-subscriptions';
22
import { Store } from '@state-less/react-server';
3+
import express from 'express';
4+
import cors from 'cors';
35
import logger from './lib/logger';
46
import { NotificationEngine } from './lib/NotificationEngine';
57

@@ -12,6 +14,7 @@ export const notificationEngine = new NotificationEngine({
1214
interval: 1 * 60 * 1000,
1315
logger,
1416
});
15-
17+
export const app = express();
18+
app.use(cors());
1619
store.sync(20 * 1000);
1720
notificationEngine.start();

src/lib/NotificationEngine.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
getSeconds,
1212
getYear,
1313
} from 'date-fns';
14+
import jwt from 'jwt-simple';
15+
import { JWT_SECRET } from '../config';
1416
const itemTypeStateKeyMap = {
1517
Todo: 'todo',
1618
Counter: 'counter',
@@ -50,19 +52,24 @@ const checkTodo = (todo, client) => {
5052
new Date(lastNotifiedClient || 0),
5153
new Date()
5254
);
53-
console.log('Todo', todo.title, timeAtDueDate, todo.lastNotified, client);
5455
if (!completed && sameDate && sameTime && lastNotified < -15) {
5556
return true;
5657
}
5758
};
5859

5960
export class NotificationEngine {
6061
_store: Store;
62+
6163
_interval: number;
64+
6265
_timeout: any;
66+
6367
_listsKey: string;
68+
6469
_webpushKey: string;
70+
6571
_clients: Array<{ id: string; sub: any; user: any }>;
72+
6673
_logger: any;
6774

6875
constructor({ store, interval, listsKey, webpushKey, logger }) {
@@ -134,9 +141,13 @@ export class NotificationEngine {
134141
scope: `${todo.id}.${user?.id || clientId}`,
135142
});
136143
Object.assign(todo, stored.value);
137-
console.log('Checking Todo', stored.value.title);
144+
const token = jwt.encode(user, JWT_SECRET);
138145
if (checkTodo(stored.value, clientId)) {
139146
this.sendNotification(sub, {
147+
token,
148+
clientId,
149+
id: stored.value.id,
150+
actions: ['complete'],
140151
title: stored.value.title,
141152
body: `It's almost ${format(
142153
new Date(stored.value.dueTime),
@@ -152,6 +163,7 @@ export class NotificationEngine {
152163
});
153164
}
154165
}
166+
155167
sendNotification(sub, body) {
156168
if (typeof body !== 'string') {
157169
body = JSON.stringify(body);
@@ -166,6 +178,7 @@ export class NotificationEngine {
166178
this._logger.error`Error sending notification: ${e}`;
167179
}
168180
}
181+
169182
start() {
170183
this._timeout = setInterval(() => this.run(), this._interval);
171184
this._logger.info`Started Interval`;

src/resolvers.ts

-2
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ const unmountComponent = (parent, args, context) => {
9090
const cleanup = Dispatcher.getCurrent().getCleanupFns(
9191
clientKey(key, context)
9292
);
93-
console.log('Unmounting', key, cleanup?.length);
9493
const len = cleanup?.length || 0;
9594
cleanup.forEach((fn) => fn());
9695
return len;
@@ -99,7 +98,6 @@ const unmountComponent = (parent, args, context) => {
9998
const mountComponent = (parent, args, context) => {
10099
const { key, props } = args;
101100

102-
console.log('Mountint', key);
103101
const component = globalInstance.components.get(key);
104102

105103
try {

0 commit comments

Comments
 (0)