stable andd prepping for the vite changeover

main
Shoofle 3 weeks ago
parent 1e0eb1e831
commit f6ca0f923a
  1. 107
      client/package-lock.json
  2. 6
      client/package.json
  3. 56
      client/src/App.css
  4. 12
      client/src/App.js
  5. 57
      client/src/Landing.jsx
  6. 16
      client/src/apiTools.jsx
  7. 13
      client/src/index.css
  8. 1
      client/src/index.js
  9. 29
      client/src/landing/GraphRender.jsx
  10. 30
      client/src/landing/Landing.css
  11. 49
      client/src/landing/Landing.jsx
  12. 26
      client/src/landing/PageList.jsx
  13. 70
      client/src/page/PageEdit.jsx
  14. 34
      client/src/page/PageView.jsx
  15. 31
      client/src/page/Pages.css
  16. BIN
      db/the_big_db.db/dbs/default/data-wal
  17. 2
      db/the_big_db.db/dbs/default/stats.json
  18. BIN
      db/the_big_db.db/dbs/default/wallog
  19. 26
      server/graphStuff.js
  20. 519
      server/package-lock.json
  21. 5
      server/package.json
  22. 23
      server/server.js

@ -9,14 +9,20 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@libsql/client": "^0.11.0", "@libsql/client": "^0.11.0",
"@react-sigma/core": "^4.0.3",
"@tanstack/react-query": "^5.56.2", "@tanstack/react-query": "^5.56.2",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"graphology": "^0.25.4",
"graphology-layout": "^0.6.1",
"graphology-layout-force": "^0.2.4",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-router-dom": "^6.26.2", "react-router-dom": "^6.26.2",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"react-sigma": "^1.2.35",
"sigma": "^3.0.0-beta.29",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
} }
}, },
@ -3461,6 +3467,16 @@
} }
} }
}, },
"node_modules/@react-sigma/core": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@react-sigma/core/-/core-4.0.3.tgz",
"integrity": "sha512-/y/U1GH18xjGYMWNYXXqHGJ+8tHf+e4z1i0gNSm9iuhch8sGFJooO3VfyPJlBox42Wl4/gCapQhOHAfVW0pRtw==",
"peerDependencies": {
"graphology": "^0.25.4",
"react": "^18.0.0",
"sigma": "^3.0.0-beta.24"
}
},
"node_modules/@remix-run/router": { "node_modules/@remix-run/router": {
"version": "1.19.2", "version": "1.19.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz",
@ -9246,6 +9262,55 @@
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
}, },
"node_modules/graphology": {
"version": "0.25.4",
"resolved": "https://registry.npmjs.org/graphology/-/graphology-0.25.4.tgz",
"integrity": "sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==",
"dependencies": {
"events": "^3.3.0",
"obliterator": "^2.0.2"
},
"peerDependencies": {
"graphology-types": ">=0.24.0"
}
},
"node_modules/graphology-layout": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/graphology-layout/-/graphology-layout-0.6.1.tgz",
"integrity": "sha512-m9aMvbd0uDPffUCFPng5ibRkb2pmfNvdKjQWeZrf71RS1aOoat5874+DcyNfMeCT4aQguKC7Lj9eCbqZj/h8Ag==",
"dependencies": {
"graphology-utils": "^2.3.0",
"pandemonium": "^2.4.0"
},
"peerDependencies": {
"graphology-types": ">=0.19.0"
}
},
"node_modules/graphology-layout-force": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/graphology-layout-force/-/graphology-layout-force-0.2.4.tgz",
"integrity": "sha512-NYZz0YAnDkn5pkm30cvB0IScFoWGtbzJMrqaiH070dYlYJiag12Oc89dbVfaMaVR/w8DMIKxn/ix9Bqj+Umm9Q==",
"dependencies": {
"graphology-utils": "^2.4.2"
},
"peerDependencies": {
"graphology-types": ">=0.19.0"
}
},
"node_modules/graphology-types": {
"version": "0.24.7",
"resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.7.tgz",
"integrity": "sha512-tdcqOOpwArNjEr0gNQKCXwaNCWnQJrog14nJNQPeemcLnXQUUGrsCWpWkVKt46zLjcS6/KGoayeJfHHyPDlvwA==",
"peer": true
},
"node_modules/graphology-utils": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/graphology-utils/-/graphology-utils-2.5.2.tgz",
"integrity": "sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ==",
"peerDependencies": {
"graphology-types": ">=0.23.0"
}
},
"node_modules/gzip-size": { "node_modules/gzip-size": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
@ -12913,6 +12978,14 @@
"mkdirp": "bin/cmd.js" "mkdirp": "bin/cmd.js"
} }
}, },
"node_modules/mnemonist": {
"version": "0.39.8",
"resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz",
"integrity": "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==",
"dependencies": {
"obliterator": "^2.0.1"
}
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -13242,6 +13315,11 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/obliterator": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz",
"integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ=="
},
"node_modules/obuf": { "node_modules/obuf": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
@ -13370,6 +13448,14 @@
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==" "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
}, },
"node_modules/pandemonium": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/pandemonium/-/pandemonium-2.4.1.tgz",
"integrity": "sha512-wRqjisUyiUfXowgm7MFH2rwJzKIr20rca5FsHXCMNm1W5YPP1hCtrZfgmQ62kP7OZ7Xt+cR858aB28lu5NX55g==",
"dependencies": {
"mnemonist": "^0.39.2"
}
},
"node_modules/param-case": { "node_modules/param-case": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
@ -15378,6 +15464,18 @@
} }
} }
}, },
"node_modules/react-sigma": {
"version": "1.2.35",
"resolved": "https://registry.npmjs.org/react-sigma/-/react-sigma-1.2.35.tgz",
"integrity": "sha512-amjKjaQusefDp8dD9er3wE/lfubSQT5fJSRF4SsS0LjxRue4bpSapAQ1WpmSM6nQEgawbrKel+bQBmatl7MAIw==",
"engines": {
"node": ">=8.0"
},
"peerDependencies": {
"react": ">=15.3",
"react-dom": ">=15.3"
}
},
"node_modules/read-cache": { "node_modules/read-cache": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -16222,6 +16320,15 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/sigma": {
"version": "3.0.0-beta.29",
"resolved": "https://registry.npmjs.org/sigma/-/sigma-3.0.0-beta.29.tgz",
"integrity": "sha512-3VpLIEnuDiZbO7XPnsyyUwUoTOI9u5aDq9nIPvc/eO3FHE/z7P7gCvfmAu2JB1635gZSLwt39mvDpYTXZ8+jaQ==",
"dependencies": {
"events": "^3.3.0",
"graphology-utils": "^2.5.2"
}
},
"node_modules/signal-exit": { "node_modules/signal-exit": {
"version": "3.0.7", "version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",

@ -4,14 +4,20 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@libsql/client": "^0.11.0", "@libsql/client": "^0.11.0",
"@react-sigma/core": "^4.0.3",
"@tanstack/react-query": "^5.56.2", "@tanstack/react-query": "^5.56.2",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"graphology": "^0.25.4",
"graphology-layout": "^0.6.1",
"graphology-layout-force": "^0.2.4",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-router-dom": "^6.26.2", "react-router-dom": "^6.26.2",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"react-sigma": "^1.2.35",
"sigma": "^3.0.0-beta.29",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
"scripts": { "scripts": {

@ -1,50 +1,18 @@
.App { body {
text-align: center; background: rgb(0,131,77);
padding-left: 60vmin; background: radial-gradient(circle, rgba(0,131,77,1) 0%, rgba(17,66,0,1) 100%);
padding-right: 60vmin; font-family: sans-serif;
background-color: #282c34;
} }
.App-logo { button {
height: 10vmin; border-radius: 1rem;
pointer-events: none; padding: 1rem;
margin: 1rem;
} }
@media (prefers-reduced-motion: no-preference) { a {
.App-logo { color: lightslategrey;
animation: App-logo-spin infinite 20s linear;
}
} }
a:visited {
.App-header { color: slategray;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.Page-contents {
text-align: left;
padding: 1em;
border: 1px solid black;
width: 100%;
}
.Page-contents pre {
width: 100%;
} }

@ -1,8 +1,10 @@
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import Landing from './Landing.jsx' import { BrowserRouter, Routes, Route } from 'react-router-dom';
import PageView from './PageView.jsx' import Landing from './landing/Landing.jsx'
import EditPage from './EditPage.jsx' import PageView from './page/PageView.jsx'
import PageEdit from './page/PageEdit.jsx'
import './App.css'
const queryClient = new QueryClient(); const queryClient = new QueryClient();
@ -13,7 +15,7 @@ function App() {
<Routes> <Routes>
<Route path="/" element={<Landing/>}/> <Route path="/" element={<Landing/>}/>
<Route path="/:pagenumber" element={<PageView/>}/> <Route path="/:pagenumber" element={<PageView/>}/>
<Route path="/:pagenumber/edit" element={<EditPage/>}/> <Route path="/:pagenumber/edit" element={<PageEdit/>}/>
</Routes> </Routes>
</BrowserRouter> </BrowserRouter>
</QueryClientProvider> </QueryClientProvider>

@ -1,57 +0,0 @@
import logo from './logo.svg';
import './App.css';
import { useNavigate, Link } from 'react-router-dom'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { apiUrl, fetchPageList, postNewPage } from './apiTools.jsx';
import { useFixLinks } from './clientStuff.jsx';
function Landing() {
const queryClient = useQueryClient();
const navigate = useNavigate();
const noLoad = useFixLinks();
const { isPending, error, data } = useQuery({ // fetch the currrent values
queryKey: ['pages'],
queryFn: fetchPageList
})
const makeNewPage = useMutation({ // for changing the value when we're done with it
mutationFn: () => postNewPage()
.catch((error) => { console.log("got an error", error.json()) }),
onSettled: async (data, error, variables) => {
// Invalidate and navigate to the new page
await queryClient.invalidateQueries({ queryKey: ['pages'] });
navigate(`/${data}/edit`);
},
});
return (
<div className="App">
<header className="App-header">
<a href="/"><img src={logo} className="App-logo" alt="logo" /></a>
<h3>Welcome to the forest!</h3>
<h4>This is some random stuff I added!</h4>
{ isPending ?
"Loading..." :
(error ?
<pre>{error}</pre>
:
<ul>
{
data.map((row) =>
<li key={row}>
<a href={`/${row}`} {...noLoad}>page {row}</a>
</li>
)
}
</ul>
)
}
<button onClick={makeNewPage.mutate} >Dig!</button>
</header>
</div>
);
}
export default Landing;

@ -1,3 +1,5 @@
import Graph from 'graphology';
// This is wrapper functtions to do requests to the api, from the frontend. // This is wrapper functtions to do requests to the api, from the frontend.
export const apiUrl = "http://127.0.0.1:3001" export const apiUrl = "http://127.0.0.1:3001"
@ -42,4 +44,16 @@ export async function deletePage(id) {
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
...defaults ...defaults
}) })
} }
export async function fetchGraph() {
return fetch(`${apiUrl}/graph`, {
method: 'GET',
...defaults
}).then((res) => res.json())
.then((serialized) => {
const graph = new Graph();
graph.import(serialized);
return graph;
})
}

@ -1,13 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App'; import App from './App';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';

@ -0,0 +1,29 @@
import { SigmaContainer } from "@react-sigma/core";
import "@react-sigma/core/lib/react-sigma.min.css";
import { useQuery } from '@tanstack/react-query';
import { fetchGraph } from '../apiTools.jsx';
import ForceSupervisor from 'graphology-layout-force/worker';
export default function GraphRender() {
const { isPending, error, data } = useQuery({ // fetch the currrent values
queryKey: ['graph'],
queryFn: fetchGraph
});
let outputStuff;
if (isPending) outputStuff = "Loading...";
else if (error) outputStuff = `Error encountered: ${error}`;
else outputStuff = (
<SigmaContainer
allowInvalidContainer
graph={data}
style={{height: "400px", width: "100%", background: 'rgba(0,0,0,0)'}}
/>
);
if (data) (new ForceSupervisor(data, {allowInvalidContainer: true})).start();
return outputStuff;
}

@ -0,0 +1,30 @@
.landing-page {
padding-left:10rem;
padding-right: 10rem;
padding-top: 3rem;
color: white;
}
.landing-page header {
text-align: right;
width: 100%;
}
.landing-page header hr {
width: 60%;
margin-inline-start: auto;
margin-inline-end: 0;
color: white;
}
.landing-container {
display: grid;
width: 100%;
grid-template-columns: repeat(3, 1fr);
}
.landing-section {
margin: 1rem;
padding: 1rem;
border-radius: 1rem;
color: white;
background: rgba(10,66,30, 0.75);
}

@ -0,0 +1,49 @@
import { useNavigate, Link } from 'react-router-dom'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { fetchPageList, postNewPage } from '../apiTools.jsx';
import { useFixLinks } from '../clientStuff.jsx';
import PageList from './PageList.jsx';
import GraphRender from './GraphRender.jsx';
import './Landing.css'
function Landing() {
const queryClient = useQueryClient();
const navigate = useNavigate();
const makeNewPage = useMutation({ // for changing the value when we're done with it
mutationFn: () => postNewPage()
.catch((error) => { console.log("got an error", error.json()) }),
onSettled: async (data, error, variables) => {
// Invalidate and navigate to the new page
await queryClient.invalidateQueries({ queryKey: ['pages'] });
navigate(`/${data}/edit`);
},
});
return (
<div className="landing-page">
<header>
<h1>Welcome to the forest.</h1>
<hr/>
</header>
<div className="landing-container">
<section className="landing-section">
<GraphRender />
</section>
<section className="landing-section">
<PageList />
</section>
<section className="landing-section">
<button>Sign up</button><br/>
<button>Log in</button><br/>
<button onClick={makeNewPage.mutate} >Dig new room!</button><br/>
</section>
</div>
</div>
);
}
export default Landing;

@ -0,0 +1,26 @@
import { useQuery } from '@tanstack/react-query';
import { fetchPageList } from '../apiTools.jsx';
import { useFixLinks } from '../clientStuff.jsx';
export default function PageList({pages, ...props}) {
const noLoad = useFixLinks();
const { isPending, error, data } = useQuery({ // fetch the currrent values
queryKey: ['pages'],
queryFn: fetchPageList
});
let outputContents = "";
if (isPending) outputContents = "Loading...";
else if (error) outputContents = `Error encountered: ${error}`;
else outputContents = (
<ol {...props}>
{ data.map(({id, title}) =>
<li key={id} >
<a href={`/${id}`} {...noLoad} >{title}</a>
</li>
)}
</ol>
);
return outputContents;
}

@ -1,10 +1,8 @@
import logo from './logo.svg';
import { App } from './App.js';
import './App.css';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { useState } from 'react'; import { apiUrl, fetchPage, postPage, deletePage } from '../apiTools.jsx';
import { apiUrl, fetchPage, postPage, deletePage } from './apiTools.jsx';
import './Pages.css';
function PageView() { function PageView() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -57,40 +55,40 @@ function PageView() {
} }
return ( return (
<div className="App"> <div className="page-container">
<section className="App-header"> <header>
<a href="/"><img src={logo} className="App-logo" alt="logo" /></a> <h1>
<div className="Page-title"> {pagenumber}.&nbsp;
<h3> <span
{pagenumber}.
<span
contentEditable="true" contentEditable="true"
dangerouslySetInnerHTML={{__html: ready ? page_title : "..." }} /> dangerouslySetInnerHTML={{__html: ready ? page_title : "..." }} />
</h3> </h1>
</div> <hr/>
{ ready ? </header>
<> <section>
<div className="Page-contents"> { ready ?
<pre <>
contentEditable="true" <div className="page-contents">
dangerouslySetInnerHTML={{__html: page_text}} /> <pre
</div> contentEditable="true"
<button dangerouslySetInnerHTML={{__html: page_text}} />
disabled={postMutation.isPending}
onClick={submitChanges}>
{postMutation.isPending ? "Updating..." : "Update"}
</button>
<button
disabled={deleteMutation.isPending}
onClick={submitDelete}>
{deleteMutation.isPending ? "Deleting..." : "Delete this page (no backsies)"}
</button>
</>
:
<div className="Page-contents">
{ fetchQuery.isPending ? "Loading..." : JSON.stringify(fetchQuery.error) }
</div> </div>
} <button
disabled={postMutation.isPending}
onClick={submitChanges}>
{postMutation.isPending ? "Updating..." : "Update"}
</button>
<button
disabled={deleteMutation.isPending}
onClick={submitDelete}>
{deleteMutation.isPending ? "Deleting..." : "Delete this page (no backsies)"}
</button>
</>
:
<div className="page-contents">
{ fetchQuery.isPending ? "Loading..." : JSON.stringify(fetchQuery.error) }
</div>
}
</section> </section>
</div> </div>
); );

@ -1,12 +1,9 @@
import logo from './logo.svg';
import { App } from './App.js';
import './App.css';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { useState } from 'react'; import { apiUrl, fetchPage, postPage } from '../apiTools.jsx';
import { apiUrl, fetchPage, postPage } from './apiTools.jsx'; import { useFixLinks } from '../clientStuff.jsx';
import { useFixLinks } from './clientStuff.jsx';
import './Pages.css';
function PageView() { function PageView() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -25,26 +22,25 @@ function PageView() {
if (data) [the_id, page_title, page_text, page_html] = data; if (data) [the_id, page_title, page_text, page_html] = data;
return ( return (
<div className="App"> <div className="page-container">
<header className="App-header"> <div>
<a href="/" {...noLoad}><img src={logo} className="App-logo" alt="logo" /></a> <header>
<div className="Page-title"> <h1>{pagenumber}. {ready ? (page_title || " ") : "..."}</h1>
<h3>{pagenumber}. {ready ? (page_title || " ") : "..."}</h3> <hr/>
</div> </header>
<section>
{ ready ? { ready ?
<div <div
className="Page-contents" className="page-contents"
dangerouslySetInnerHTML={{ __html: (page_html || " ") }} dangerouslySetInnerHTML={{ __html: (page_html || " ") }}
{...noLoad} {...noLoad}
/> />
: :
<div className="Page-contents"> (isPending ? "Loading..." : JSON.stringify(error))
{ isPending ? "Loading..." : JSON.stringify(error) }
</div>
} }
<button onClick={(e) => { e.preventDefault(); navigate(`/${pagenumber}/edit`)}}>Edit this page!</button>
<button type="submit" onClick={() => navigate(`/${pagenumber}/edit`)}>Edit this page!</button> </section>
</header> </div>
</div> </div>
); );
} }

@ -0,0 +1,31 @@
.page-container {
padding-left:10rem;
padding-right: 10rem;
padding-top: 3rem;
color: white;
}
.page-container header {
text-align: right;
width: 100%;
}
.page-container header hr {
width: 60%;
margin-inline-start: auto;
margin-inline-end: 0;
color: white;
}
.page-contents {
text-align: left;
background: rgba(17,66,0);
margin: 1rem;
padding: 1rem;
border-radius: 1rem;
}
.page-contents pre {
width: 100%;
white-space: normal;
}

@ -1 +1 @@
{"id":"7de13931-444c-4dd2-a19a-5cd1e70b7f36","rows_written":112,"rows_read":2745,"storage_bytes_used":12288,"write_requests_delegated":0,"current_frame_no":111,"top_query_threshold":3,"top_queries":[{"rows_written":2,"rows_read":1,"query":"\ncreate table if not exists renders (\n id integer primary key,\n title varchar(255),\n description text\n)\n"},{"rows_written":0,"rows_read":4,"query":"select id from pages"},{"rows_written":0,"rows_read":5,"query":"select id from pages"},{"rows_written":1,"rows_read":4,"query":"\nalter table pages \nadd column html text\n"},{"rows_written":0,"rows_read":6,"query":"\nselect p.id from pages p where not exists (select r.id from renders r where p.id = r.id)\n"},{"rows_written":0,"rows_read":10,"query":"select id from pages"},{"rows_written":0,"rows_read":11,"query":"select id from pages"},{"rows_written":0,"rows_read":12,"query":"select id from pages"},{"rows_written":0,"rows_read":13,"query":"select id from pages"},{"rows_written":0,"rows_read":14,"query":"select id from pages"}],"slowest_query_threshold":0,"slowest_queries":[{"elapsed_ms":0,"query":"select title from pages","rows_written":0,"rows_read":1},{"elapsed_ms":0,"query":"select title from pages","rows_written":0,"rows_read":2},{"elapsed_ms":1,"query":"\nalter table pages \nadd column html text\n","rows_written":1,"rows_read":4},{"elapsed_ms":1,"query":"\ncreate table if not exists renders (\n id integer primary key,\n title varchar(255),\n description text\n)\n","rows_written":2,"rows_read":1},{"elapsed_ms":1,"query":"insert into pages (title, description) values (?, ?) returning id","rows_written":1,"rows_read":1},{"elapsed_ms":1,"query":"replace into pages (id, title, description) values (?, ?, ?)","rows_written":1,"rows_read":1},{"elapsed_ms":1,"query":"replace into pages (id, title, description, html) values (?, ?, ?, ?)","rows_written":1,"rows_read":1},{"elapsed_ms":1,"query":"select * from pages where id=@val","rows_written":0,"rows_read":1},{"elapsed_ms":1,"query":"select count(*) as num_pages from pages","rows_written":0,"rows_read":1},{"elapsed_ms":7,"query":"replace into pages (id, title, description) values (?, ?, ?)","rows_written":1,"rows_read":1}],"embedded_replica_frames_replicated":0,"query_count":1006,"query_latency":98084} {"id":"7de13931-444c-4dd2-a19a-5cd1e70b7f36","rows_written":149,"rows_read":10979,"storage_bytes_used":20480,"write_requests_delegated":0,"current_frame_no":151,"top_query_threshold":15,"top_queries":[{"rows_written":0,"rows_read":15,"query":"select id, html from pages"},{"rows_written":0,"rows_read":15,"query":"select id, title from pages"},{"rows_written":0,"rows_read":16,"query":"select id, html from pages"},{"rows_written":0,"rows_read":16,"query":"select id, title from pages"},{"rows_written":0,"rows_read":17,"query":"select id, html from pages"},{"rows_written":0,"rows_read":17,"query":"select id, title from pages"},{"rows_written":0,"rows_read":18,"query":"select id, html from pages"},{"rows_written":0,"rows_read":18,"query":"select id, title from pages"},{"rows_written":0,"rows_read":19,"query":"select id, html from pages"},{"rows_written":0,"rows_read":19,"query":"select id, title from pages"}],"slowest_query_threshold":0,"slowest_queries":[{"elapsed_ms":0,"query":"select title from pages","rows_written":0,"rows_read":1},{"elapsed_ms":0,"query":"select title from pages","rows_written":0,"rows_read":2},{"elapsed_ms":1,"query":"\nalter table pages \nadd column html text\n","rows_written":1,"rows_read":4},{"elapsed_ms":1,"query":"\ncreate table if not exists renders (\n id integer primary key,\n title varchar(255),\n description text\n)\n","rows_written":2,"rows_read":1},{"elapsed_ms":1,"query":"insert into pages (title, description) values (?, ?) returning id","rows_written":1,"rows_read":1},{"elapsed_ms":1,"query":"replace into pages (id, title, description) values (?, ?, ?)","rows_written":1,"rows_read":1},{"elapsed_ms":1,"query":"replace into pages (id, title, description, html) values (?, ?, ?, ?)","rows_written":1,"rows_read":1},{"elapsed_ms":1,"query":"select * from pages where id=@val","rows_written":0,"rows_read":1},{"elapsed_ms":1,"query":"select count(*) as num_pages from pages","rows_written":0,"rows_read":1},{"elapsed_ms":7,"query":"replace into pages (id, title, description) values (?, ?, ?)","rows_written":1,"rows_read":1}],"embedded_replica_frames_replicated":0,"query_count":1820,"query_latency":153654}

@ -0,0 +1,26 @@
const { JSDOM } = require("jsdom");
const graphology = require("graphology");
const { circular } = require('graphology-layout');
function graphFromList(allTheStuff) {
const graph = new graphology.Graph();
for (const [id, html] of allTheStuff) {
graph.addNode(id);
}
for (const [id, html] of allTheStuff) {
const { document } = (new JSDOM(html)).window;
const links = document.querySelectorAll('a');
links.forEach((link) => {
const referent = link.href.replace("/","");
graph.mergeEdge(id, referent);
});
}
circular.assign(graph);
return graph;
}
module.exports = { graphFromList: graphFromList };

@ -10,6 +10,11 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"express": "^4.21.0", "express": "^4.21.0",
"graphology": "^0.25.4",
"graphology-layout": "^0.6.1",
"graphology-layout-force": "^0.2.4",
"graphology-layout-forceatlas2": "^0.10.1",
"jsdom": "^25.0.0",
"nodemon": "^3.1.5", "nodemon": "^3.1.5",
"showdown": "^2.1.0" "showdown": "^2.1.0"
} }
@ -26,6 +31,38 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/agent-base": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
"integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
"dependencies": {
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/agent-base/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/agent-base/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/anymatch": { "node_modules/anymatch": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@ -43,6 +80,11 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
}, },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -151,6 +193,17 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": { "node_modules/commander": {
"version": "9.5.0", "version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
@ -196,6 +249,29 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
}, },
"node_modules/cssstyle": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz",
"integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==",
"dependencies": {
"rrweb-cssom": "^0.7.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/data-urls": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
"integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
"dependencies": {
"whatwg-mimetype": "^4.0.0",
"whatwg-url": "^14.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/debug": { "node_modules/debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -204,6 +280,11 @@
"ms": "2.0.0" "ms": "2.0.0"
} }
}, },
"node_modules/decimal.js": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
},
"node_modules/define-data-property": { "node_modules/define-data-property": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
@ -220,6 +301,14 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/depd": { "node_modules/depd": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@ -250,6 +339,17 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-define-property": { "node_modules/es-define-property": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
@ -282,6 +382,14 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/express": { "node_modules/express": {
"version": "4.21.0", "version": "4.21.0",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
@ -351,6 +459,19 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/forwarded": { "node_modules/forwarded": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -428,6 +549,66 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/graphology": {
"version": "0.25.4",
"resolved": "https://registry.npmjs.org/graphology/-/graphology-0.25.4.tgz",
"integrity": "sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==",
"dependencies": {
"events": "^3.3.0",
"obliterator": "^2.0.2"
},
"peerDependencies": {
"graphology-types": ">=0.24.0"
}
},
"node_modules/graphology-layout": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/graphology-layout/-/graphology-layout-0.6.1.tgz",
"integrity": "sha512-m9aMvbd0uDPffUCFPng5ibRkb2pmfNvdKjQWeZrf71RS1aOoat5874+DcyNfMeCT4aQguKC7Lj9eCbqZj/h8Ag==",
"dependencies": {
"graphology-utils": "^2.3.0",
"pandemonium": "^2.4.0"
},
"peerDependencies": {
"graphology-types": ">=0.19.0"
}
},
"node_modules/graphology-layout-force": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/graphology-layout-force/-/graphology-layout-force-0.2.4.tgz",
"integrity": "sha512-NYZz0YAnDkn5pkm30cvB0IScFoWGtbzJMrqaiH070dYlYJiag12Oc89dbVfaMaVR/w8DMIKxn/ix9Bqj+Umm9Q==",
"dependencies": {
"graphology-utils": "^2.4.2"
},
"peerDependencies": {
"graphology-types": ">=0.19.0"
}
},
"node_modules/graphology-layout-forceatlas2": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/graphology-layout-forceatlas2/-/graphology-layout-forceatlas2-0.10.1.tgz",
"integrity": "sha512-ogzBeF1FvWzjkikrIFwxhlZXvD2+wlY54lqhsrWprcdPjopM2J9HoMweUmIgwaTvY4bUYVimpSsOdvDv1gPRFQ==",
"dependencies": {
"graphology-utils": "^2.1.0"
},
"peerDependencies": {
"graphology-types": ">=0.19.0"
}
},
"node_modules/graphology-types": {
"version": "0.24.7",
"resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.7.tgz",
"integrity": "sha512-tdcqOOpwArNjEr0gNQKCXwaNCWnQJrog14nJNQPeemcLnXQUUGrsCWpWkVKt46zLjcS6/KGoayeJfHHyPDlvwA==",
"peer": true
},
"node_modules/graphology-utils": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/graphology-utils/-/graphology-utils-2.5.2.tgz",
"integrity": "sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ==",
"peerDependencies": {
"graphology-types": ">=0.23.0"
}
},
"node_modules/has-flag": { "node_modules/has-flag": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -480,6 +661,17 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/html-encoding-sniffer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
"integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
"dependencies": {
"whatwg-encoding": "^3.1.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/http-errors": { "node_modules/http-errors": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@ -495,6 +687,72 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/http-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"dependencies": {
"agent-base": "^7.1.0",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/http-proxy-agent/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/http-proxy-agent/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/https-proxy-agent": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
"integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
"dependencies": {
"agent-base": "^7.0.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/https-proxy-agent/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/https-proxy-agent/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -562,6 +820,50 @@
"node": ">=0.12.0" "node": ">=0.12.0"
} }
}, },
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
},
"node_modules/jsdom": {
"version": "25.0.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.0.tgz",
"integrity": "sha512-OhoFVT59T7aEq75TVw9xxEfkXgacpqAhQaYgP9y/fDqWQCMB/b1H66RfmPm/MaeaAIU9nDwMOVTlPN51+ao6CQ==",
"dependencies": {
"cssstyle": "^4.0.1",
"data-urls": "^5.0.0",
"decimal.js": "^10.4.3",
"form-data": "^4.0.0",
"html-encoding-sniffer": "^4.0.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.5",
"is-potential-custom-element-name": "^1.0.1",
"nwsapi": "^2.2.12",
"parse5": "^7.1.2",
"rrweb-cssom": "^0.7.1",
"saxes": "^6.0.0",
"symbol-tree": "^3.2.4",
"tough-cookie": "^4.1.4",
"w3c-xmlserializer": "^5.0.0",
"webidl-conversions": "^7.0.0",
"whatwg-encoding": "^3.1.1",
"whatwg-mimetype": "^4.0.0",
"whatwg-url": "^14.0.0",
"ws": "^8.18.0",
"xml-name-validator": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"canvas": "^2.11.2"
},
"peerDependenciesMeta": {
"canvas": {
"optional": true
}
}
},
"node_modules/media-typer": { "node_modules/media-typer": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -627,6 +929,14 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/mnemonist": {
"version": "0.39.8",
"resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz",
"integrity": "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==",
"dependencies": {
"obliterator": "^2.0.1"
}
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -696,6 +1006,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/nwsapi": {
"version": "2.2.12",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.12.tgz",
"integrity": "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w=="
},
"node_modules/object-inspect": { "node_modules/object-inspect": {
"version": "1.13.2", "version": "1.13.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
@ -707,6 +1022,11 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/obliterator": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz",
"integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ=="
},
"node_modules/on-finished": { "node_modules/on-finished": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@ -718,6 +1038,25 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/pandemonium": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/pandemonium/-/pandemonium-2.4.1.tgz",
"integrity": "sha512-wRqjisUyiUfXowgm7MFH2rwJzKIr20rca5FsHXCMNm1W5YPP1hCtrZfgmQ62kP7OZ7Xt+cR858aB28lu5NX55g==",
"dependencies": {
"mnemonist": "^0.39.2"
}
},
"node_modules/parse5": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
"integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
"dependencies": {
"entities": "^4.4.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parseurl": { "node_modules/parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@ -754,11 +1093,24 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
},
"node_modules/pstree.remy": { "node_modules/pstree.remy": {
"version": "1.1.8", "version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="
}, },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"engines": {
"node": ">=6"
}
},
"node_modules/qs": { "node_modules/qs": {
"version": "6.13.0", "version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
@ -773,6 +1125,11 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
},
"node_modules/range-parser": { "node_modules/range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@ -806,6 +1163,16 @@
"node": ">=8.10.0" "node": ">=8.10.0"
} }
}, },
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
},
"node_modules/rrweb-cssom": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz",
"integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg=="
},
"node_modules/safe-buffer": { "node_modules/safe-buffer": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@ -830,6 +1197,17 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
}, },
"node_modules/saxes": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
"integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
"dependencies": {
"xmlchars": "^2.2.0"
},
"engines": {
"node": ">=v12.22.7"
}
},
"node_modules/semver": { "node_modules/semver": {
"version": "7.6.3", "version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
@ -974,6 +1352,11 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -1001,6 +1384,31 @@
"nodetouch": "bin/nodetouch.js" "nodetouch": "bin/nodetouch.js"
} }
}, },
"node_modules/tough-cookie": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
"integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
"dependencies": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
"universalify": "^0.2.0",
"url-parse": "^1.5.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tr46": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
"integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
"dependencies": {
"punycode": "^2.3.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/type-is": { "node_modules/type-is": {
"version": "1.6.18", "version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@ -1018,6 +1426,14 @@
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
}, },
"node_modules/universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/unpipe": { "node_modules/unpipe": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@ -1026,6 +1442,15 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/url-parse": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
}
},
"node_modules/utils-merge": { "node_modules/utils-merge": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@ -1041,6 +1466,100 @@
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
} }
},
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
"integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
"dependencies": {
"xml-name-validator": "^5.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
"engines": {
"node": ">=12"
}
},
"node_modules/whatwg-encoding": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
"dependencies": {
"iconv-lite": "0.6.3"
},
"engines": {
"node": ">=18"
}
},
"node_modules/whatwg-encoding/node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/whatwg-mimetype": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
"engines": {
"node": ">=18"
}
},
"node_modules/whatwg-url": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz",
"integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==",
"dependencies": {
"tr46": "^5.0.0",
"webidl-conversions": "^7.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xml-name-validator": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
"engines": {
"node": ">=18"
}
},
"node_modules/xmlchars": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
} }
} }
} }

@ -15,6 +15,11 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"express": "^4.21.0", "express": "^4.21.0",
"graphology": "^0.25.4",
"graphology-layout": "^0.6.1",
"graphology-layout-force": "^0.2.4",
"graphology-layout-forceatlas2": "^0.10.1",
"jsdom": "^25.0.0",
"nodemon": "^3.1.5", "nodemon": "^3.1.5",
"showdown": "^2.1.0" "showdown": "^2.1.0"
} }

@ -1,6 +1,7 @@
const express = require('express'); const express = require('express');
const showdown = require('showdown'); const showdown = require('showdown');
const { query } = require('./dbHelper.js'); const { query } = require('./dbHelper.js');
const { graphFromList } = require('./graphStuff.js');
const port = process.env.PORT || 3001; // Use the port provided by the host or default to 3000 const port = process.env.PORT || 3001; // Use the port provided by the host or default to 3000
@ -20,26 +21,26 @@ app.listen(port, () => {
}); });
app.get('/pages', (req, res) => { app.get('/pages', (req, res) => {
query('select id from pages', []) query('select id, title from pages', [])
.then((r) => r.rows) .then((r) => r.rows)
.then((row) => row.map(([id]) => id)) .then((row) => row.map(([id, title]) => {return {"id": id, "title": title};}))
.then((ids) => res.status(200).json(ids)) .then((pages) => res.status(200).json(pages))
.catch((error) => res.status(500).json({"error": error})); .catch((error) => res.status(500).json({"error": error}));
}); });
app.post('/page/new', (req, res) => { app.post('/page/new', (req, res) => {
query('insert into pages (title, description) values (?, ?) returning id', query('insert into pages (title, description) values (?, ?) returning id',
["new page", "this page is new!"]) ["new page", "this page is new!"])
.then((r) => r.rows[0][0]) .then((r) => r.rows[0])
.then((row) => res.status(200).json({id: row})) .then((row) => res.status(200).json({id: row[0]}))
.catch((error) => res.status(500).json({"error": error})) .catch((error) => res.status(500).json({"error": error}))
}); });
app.get('/page/:id', (req, res) => { app.get('/page/:id', (req, res) => {
query('select * from pages where id=?', [req.params.id]) query('select * from pages where id=?', [req.params.id])
.then((r) => { .then((r) => {
if (r.rows.length == 0) return res.status(404).json({"error": "page not found in db"}); if (r.rows.length == 0) res.status(404).json({"error": "page not found in db"});
return r.rows[0]; else return r.rows[0];
}).then((row) => res.status(200).json(row)) }).then((row) => res.status(200).json(row))
.catch((error) => res.status(500).json({"error": error})); .catch((error) => res.status(500).json({"error": error}));
}); });
@ -57,3 +58,11 @@ app.delete('/page/:id', (req, res) => {
.then(() => res.status(200).json({id: req.params.id})) .then(() => res.status(200).json({id: req.params.id}))
.catch((error) => res.status(500).json({"error": error})) .catch((error) => res.status(500).json({"error": error}))
}) })
app.get('/graph', (req, res) => {
query('select id, html from pages', [])
.then((r) => r.rows)
.then((stuff) => graphFromList(stuff))
.then((graph) => res.json(graph))
.catch((error) => res.status(500).json({"error": error}));
});
Loading…
Cancel
Save