CRUD operations for Posts

- added all CRUD operations for Post model
- add() also creates POST_BY_MEMBER and POST_FROM_NETWORK edges
- delete() also deletes associated edges
- delete() currently does not reallocate node/edge indices after deleting
- will write the data to the db.json file and global.db.nodes
This commit is contained in:
2026-02-08 22:34:18 -05:00
parent d6520bf007
commit aaf9d56b1b
10 changed files with 342 additions and 35 deletions

View File

@@ -63,6 +63,39 @@ export default class Database {
}
}
deleteNode(prefix, id) {
try {
let model = nodeModels[prefix]
if(model) {
this.nodes[model.indices[0] + (id - 1)] = 0
} else {
throw new Error("Type does not exist for node: " + id)
}
} catch(e) {
throw e
}
}
editNode(prefix, id, toEdit) {
try {
let model = nodeModels[prefix]
if(model) {
let schema = model.schema
let result = schema.safeParse(toEdit)
if(result.success) {
this.nodes[model.indices[0] + (id - 1)] = toEdit
} else {
console.error(result.error)
throw new global.ServerError(400, "Invalid Data!");
}
} else {
throw new Error("Type does not exist for node: " + prefix)
}
} catch(e) {
throw e
}
}
addEdge(prefix, edge) {
try {
let type = prefix
@@ -83,7 +116,22 @@ export default class Database {
throw new global.ServerError(400, "Invalid Data!");
}
} else {
throw new Error("Type does not exist for edge: " + id)
throw new Error("Type does not exist for edge: " + prefix)
}
} catch(e) {
throw e
}
}
deleteEdge(prefix, id) {
try {
let model = edgeModels[prefix]
if(model) {
console.log(model.indices[0] + (id - 1))
this.edges[model.indices[0] + (id - 1)] = 0
console.log(this.edges[model.indices[0] + (id - 1)])
} else {
throw new Error("Type does not exist for edge: " + prefix)
}
} catch(e) {
throw e

View File

@@ -1,13 +1,21 @@
export default class Edge {
add(n) {
splitEdge(n) {
let toPrefix = n.to.split("-")[0]
let fromPrefix = n.from.split("-")[0]
let type = n.type
let prefix = `${fromPrefix}_${type}_${toPrefix}`
return `${fromPrefix}_${type}_${toPrefix}`
}
add(n) {
let prefix = this.splitEdge(n)
global.db.addEdge(prefix, n)
}
delete(n) {
let prefix = this.splitEdge(n)
global.db.deleteEdge(prefix, n.from.split("-")[1])
}
getByFrom(fromID) {
let result = []
for(let i = 0; i < this.entries.length; i++) {

View File

@@ -0,0 +1,37 @@
import { z } from 'zod'
export default class POST_BY_MEMBER {
prefix = "POST_BY_MEMBER"
indices = null
constructor(indices) {
this.indices = indices
}
schema = z.object({
id: z.number(),
type: z.string(),
from: z.string(),
to: z.string(),
created: z.string()
})
.strict()
getAuthorId(postId) {
for (let i = this.indices[0]; i < this.indices[1]; i++) {
if (global.db.edges[i].from === postId) {
return Number(global.db.edges[i].to.split("-")[1]);
}
}
}
getByMember(stringID) {
let result = []
for(let i = this.indices[0]; i < this.indices[1]; i++) {
if(global.db.edges[i].from === stringID) {
result.push(global.db.edges[i])
}
}
return result
}
}

View File

@@ -0,0 +1,29 @@
import { z } from 'zod'
export default class POST_FROM_NETWORK {
prefix = "POST_FROM_NETWORK"
indices = null
constructor(indices) {
this.indices = indices
}
schema = z.object({
id: z.number(),
type: z.string(),
from: z.string(),
to: z.string(),
created: z.string()
})
.strict()
getByNetwork(id) {
let result = []
for(let i = this.indices[0]; i < this.indices[1]; i++) {
if(global.db.edges[i].to === `${global.db.networks.prefix}-${id}`) {
result.push(global.db.edges[i])
}
}
return result
}
}

View File

@@ -1,6 +1,7 @@
import { z } from 'zod';
export default class Post {
prefix = "POST"
indices = null
constructor(indices) {
@@ -8,13 +9,16 @@ export default class Post {
}
schema = z.object({
id: z.number(),
text: z.string(),
time: z.string(),
sentBy: z.string()
sentBy: z.string(),
edited: z.boolean()
})
makeID(forum, number) {
return `POST-${forum}-${number}`
// return `POST-${forum}-${number}`
return `POST-${number}`
}
save(post, id) {
@@ -32,30 +36,130 @@ export default class Post {
}
}
get(forum, number) {
get(forum, by, authorId = null) {
let result = []
let limit = Math.min(number, this.entries.length)
for(let i=1; i<=limit; i++) {
let id = this.makeID(forum, i)
let post = this.entries[this.ids[id]]
let {firstName, lastName} = global.db.members.get(post.sentBy)
let seededObj = {
...post
}
seededObj.sentByID = post.sentBy
seededObj.sentBy = firstName + " " + lastName
result.push(seededObj)
if (by === "member" && authorId) {
result = this.getByMember(authorId)
} else { // network
let { id: networkId } = global.db.networks.getByAbbreviation(forum)
result = this.getByNetwork(networkId)
}
return result
}
getByID(id) {
if(typeof id === 'string') {
id = id.split("-")[1]
}
return {
id,
authorId: this.getAuthor(id),
...global.db.nodes[this.indices[0] + (id - 1)]
}
}
getAuthor(postId) {
return db.POST_BY_MEMBER.getAuthorId(`${this.prefix}-${postId}`);
}
getByNetwork(id) {
let connections = db.POST_FROM_NETWORK.getByNetwork(id)
let posts = []
connections.forEach((conn) => {
posts.push(this.getByID(conn.from))
})
return posts
}
getByMember(stringID) {
let connections = db.POST_BY_MEMBER.getByMember(stringID)
let posts = []
connections.forEach((conn) => {
posts.push(this.getByID(conn.from))
})
return posts
}
async edit(id, text, userEmail) {
try {
let userId = global.db.members.getByEmail(userEmail).id
let postToEdit = this.getByID(id)
if (postToEdit.authorId !== userId) {
throw new global.ServerError(401, "Not authorized to delete post!")
}
postToEdit.text = text
postToEdit.time = global.currentTime()
postToEdit.edited = true;
let { authorId, ...rest } = postToEdit
global.db.editNode(this.prefix, id, rest)
return postToEdit
} catch(e) {
console.error(e)
throw new global.ServerError(400, "Failed to delete post!")
}
}
async delete(id, forum, userEmail) {
try {
let userId = global.db.members.getByEmail(userEmail).id
let network = global.db.networks.getByAbbreviation(forum)
if (this.getAuthor(id) !== userId) {
throw new global.ServerError(401, "Not authorized to delete post!")
}
global.db.deleteNode(this.prefix, id)
if(network) {
global.db.edge.delete({
type: "FROM",
from: `${this.prefix}-${id}`,
to: `NETWORK-${network.id}`
})
}
global.db.edge.delete({
type: "BY",
from: `${this.prefix}-${id}`,
to: `MEMBER-${userId}`
})
} catch(e) {
console.error(e)
throw new global.ServerError(400, "Failed to delete post!")
}
}
async add(text, forum, userEmail) {
let newPost = {}
let sender = global.db.members.getByEmail(userEmail)
let network = global.db.networks.getByAbbreviation(forum)
newPost.text = text
newPost.sentBy = db.members.getIDFromEmail(userEmail)
newPost.time = global.currentTime()
let idNumber = this.entries.length+1
super.add(this.makeID(forum, idNumber), newPost)
try {
newPost.sentBy = `${sender.firstName} ${sender.lastName}`
global.db.addNode(this.prefix, newPost)
if(network) {
global.db.edge.add({
type: "FROM",
from: `${this.prefix}-${global.db.getCurrentIndex(this)}`,
to: `NETWORK-${network.id}`
})
}
global.db.edge.add({
type: "BY",
from: `${this.prefix}-${global.db.getCurrentIndex(this)}`,
to: `MEMBER-${sender.id}`
})
return `${this.prefix}-${global.db.getCurrentIndex(this)}`
} catch(e) {
console.error(e)
throw new global.ServerError(400, "Failed to add member!");
}
}
}

View File

@@ -6,6 +6,8 @@ import Post from "./forum/post.js"
import Conversation from "./dms/conversation.js"
import DM from "./dms/dm.js"
import MEMBER_IN_NETWORK from "./edges/MEMBER_IN_NETWORK.js"
import POST_FROM_NETWORK from "./edges/POST_FROM_NETWORK.js"
import POST_BY_MEMBER from "./edges/POST_BY_MEMBER.js"
let nIndices = {
"MEMBER" : [0, 0], // [id, startIndex, nextEmptyIndex
@@ -18,7 +20,9 @@ let nIndices = {
}
let eIndices = {
"MEMBER_IN_NETWORK": [0, 0]
"MEMBER_IN_NETWORK": [0, 0],
"POST_FROM_NETWORK": [4000, 4000],
"POST_BY_MEMBER": [7000, 7000]
}
export let nodeModels = {
@@ -32,5 +36,7 @@ export let nodeModels = {
}
export let edgeModels = {
MEMBER_IN_NETWORK: new MEMBER_IN_NETWORK(eIndices.MEMBER_IN_NETWORK)
MEMBER_IN_NETWORK: new MEMBER_IN_NETWORK(eIndices.MEMBER_IN_NETWORK),
POST_FROM_NETWORK: new POST_FROM_NETWORK(eIndices.POST_FROM_NETWORK),
POST_BY_MEMBER: new POST_BY_MEMBER(eIndices.POST_BY_MEMBER)
}

View File

@@ -24,6 +24,15 @@ export default class Network {
return global.db.nodes[index]
}
getByAbbreviation(abbreviation) {
for(let i=this.indices[0]; i<this.indices[1]; i++) {
if(global.db.nodes[i].abbreviation === abbreviation) {
return global.db.nodes[i]
}
}
return null
}
save(n) {
let id = `${this.prefix}-${n.id}`
let result = this.schema.safeParse(n)

View File

@@ -47,9 +47,11 @@ export let PAYMENT = z.object({
})
export let POST = z.object({
id: z.number(),
text: z.string(),
time: z.string(),
sentBy: z.string()
sentBy: z.string(),
edited: z.boolean()
})
export let CONVSRSATION = z.object({

View File

@@ -8,35 +8,99 @@ const sendSchema = z.object({
const getSchema = z.object({
forum: z.string(),
number: z.number()
by: z.string(),
authorId: z.number()
})
.strict()
const deleteSchema = z.object({
forum: z.string(),
id: z.number()
})
.strict()
const putSchema = z.object({
forum: z.string(),
id: z.number(),
text: z.string()
})
.strict()
export default class ForumHandler {
static handleSend(msg, ws) {
static async handleSend(msg, ws) {
try {
global.db.posts.add(msg.text, msg.forum, ws.userEmail)
global.Socket.broadcast({event: "new-post", app: "FORUM", forum: msg.forum, msg: this.handleGet({forum: msg.forum, number: 100})})
let postId = await global.db.posts.add(msg.text, msg.forum, ws.userEmail)
let newPost = global.db.posts.getByID(postId)
if (newPost) {
global.Socket.broadcast({
event: "new-post",
app: "FORUM",
forum: msg.forum,
msg: newPost
})
return {success: true}
} else {
return {success: false}
}
} catch(e) {
console.error(e)
}
}
static handleGet(msg) {
let data = global.db.posts.get(msg.forum, msg.number)
let data = global.db.posts.get(msg.forum, msg.by, msg.authorId)
return data
}
static handle(operation, msg, ws) {
static async handleDelete(msg, ws) {
try {
await global.db.posts.delete(msg.id, msg.forum, ws.userEmail)
global.Socket.broadcast({
event: "deleted-post",
app: "FORUM",
forum: msg.forum,
msg: msg.id
})
return {success: true}
} catch(e) {
console.error(e)
return {success: false}
}
}
static async handlePut(msg, ws) {
try {
let editedPost = await global.db.posts.edit(msg.id, msg.text, ws.userEmail)
console.log(editedPost)
if (editedPost) {
global.Socket.broadcast({
event: "edited-post",
app: "FORUM",
forum: msg.forum,
msg: editedPost
})
}
return {success: true}
} catch(e) {
console.error(e)
return {success: false}
}
}
static async handle(operation, msg, ws) {
switch(operation) {
case "SEND":
if(!sendSchema.safeParse(msg).success) throw new Error("Incorrectly formatted Forum ws message!")
return this.handleSend(msg, ws)
return await this.handleSend(msg, ws)
case "GET":
if(!getSchema.safeParse(msg).success) throw new Error("Incorrectly formatted Forum ws message!")
return this.handleGet(msg)
case "DELETE":
if(!deleteSchema.safeParse(msg).success) throw new Error("Incorrectly formatted Forum ws message!")
return this.handleDelete(msg, ws)
case "PUT":
if (!putSchema.safeParse(msg).success) throw new Error("Incorrectly formatted ws message!")
return this.handlePut(msg, ws)
}
}

View File

@@ -63,7 +63,7 @@ export default class Socket {
// Build a system where the ws obj is updated every time on navigate, so it already has context
// this way, we can only send broadcast messages to clients that actually have that app / subapp open
handleMessage = (msg, ws) => {
handleMessage = async (msg, ws) => {
try {
const text = msg.toString();
const req = JSON.parse(text);
@@ -72,7 +72,7 @@ export default class Socket {
let responseData;
switch (req.app) {
case "FORUM":
responseData = ForumHandler.handle(req.operation, req.msg, ws)
responseData = await ForumHandler.handle(req.operation, req.msg, ws)
break;
case "MESSAGES":