Skip to content

Commit

Permalink
Feat/session readonly (#25)
Browse files Browse the repository at this point in the history
* updated backend to incorporate read only sessions

* updated backend to incorporate readonly session

* fixed bug in getSessionDetails function

* updated backend with shorter ids for generated sessions

* Removed console.log and added extra layer of readonly check on backend

* changed document implementation to Map to support sublinear lookup. Added checking of shortIds generated to prevent repeat

* Add empty line at end-of-file

* Fix format

* Restore empty line

---------

Co-authored-by: Richard Dominick <[email protected]>
  • Loading branch information
wrjgold and RichDom2185 authored Mar 23, 2024
1 parent abb5be8 commit 1dd65f9
Showing 1 changed file with 37 additions and 15 deletions.
52 changes: 37 additions & 15 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,47 +12,59 @@ const app = new Koa();
const db = new ShareDB();

db.use('connect', (ctx, done) => {
// use custom to store the allowed document ID
// use custom to store the allowed document ID and readOnly setting
ctx.agent.custom = ctx.req;
done();
});
db.use('submit', (ctx, done) => {
const allowed = ctx.collection === COLLECTION_NAME && ctx.id === ctx.agent.custom;
const allowed =
ctx.collection === COLLECTION_NAME &&
ctx.id === ctx.agent.custom.docId &&
!ctx.agent.custom.readOnly;
done(allowed ? undefined : 'Cannot write to this document');
});
db.use('readSnapshots', (ctx, done) => {
const allowed =
ctx.collection === COLLECTION_NAME &&
!ctx.snapshots.find((snapshot) => snapshot.id !== ctx.agent.custom);
!ctx.snapshots.find((snapshot) => snapshot.id !== ctx.agent.custom.docId);
done(allowed ? undefined : 'Cannot read these document(s)');
});

const documents = new Set();
const documents = new Map();

app.use(cors());
app.use(websocket());
app.use(bodyParser({ enableTypes: ['json', 'text'], strict: false }));
app.use(async (ctx) => {
if (ctx.method === 'POST' && ctx.path === '/') {
const contents = isEmptyObject(ctx.request.body) ? '' : ctx.request.body;
const { contents } = ctx.request.body;

// Creates various IDs
const docId = uuid();
const doc = db.connect(undefined, docId).get(COLLECTION_NAME, docId);
const sessionEditingId = generateShortId();
documents.set(sessionEditingId, [docId, false]);
const sessionViewingId = generateShortId();
documents.set(sessionViewingId, [docId, true]);

const connection = db.connect(undefined, { docId, readOnly: false });
const doc = connection.get(COLLECTION_NAME, docId);
await new Promise((resolve, reject) => {
doc.create(contents, (err) => {
doc.create({ contents }, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
documents.add(docId);
ctx.body = docId;
ctx.body = { docId, sessionEditingId, sessionViewingId };
return;
}

const docId = ctx.path.substr(1);
if (!documents.has(docId)) {
const sessionId = ctx.path.substr(1);
const [docId, readOnly] = getSessionDetails(sessionId);

if (docId === null) {
ctx.status = 404;
return;
}
Expand All @@ -64,14 +76,24 @@ app.use(async (ctx) => {

if (ctx.ws) {
const ws = new WebSocketJSONStream(await ctx.ws());
db.listen(ws, docId);
db.listen(ws, { docId, readOnly }); // docId and readOnly is passed to 'connect' middleware as ctx.req
} else {
ctx.body = 'Document exists.';
ctx.body = { docId, readOnly };
}
});

app.listen(process.env.PORT || 8080);

function isEmptyObject(obj) {
return Object.keys(obj).length === 0 && obj.constructor === Object;
function getSessionDetails(sessionId) {
const sessionDetails = documents.get(sessionId);
return sessionDetails === undefined ? [null, null] : sessionDetails;
}

function generateShortId() {
const id = uuid().slice(0, 6);
if (documents.has(id)) {
return generateShortId();
} else {
return id;
}
}

0 comments on commit 1dd65f9

Please sign in to comment.