workin like a worker bee but not keeping track of what i do

This commit is contained in:
Shoofle 2024-09-20 12:09:16 -04:00
parent 6ce4def6e5
commit 1e0eb1e831
12 changed files with 145 additions and 154 deletions

View File

@ -1,5 +1,8 @@
.App {
text-align: center;
padding-left: 60vmin;
padding-right: 60vmin;
background-color: #282c34;
}
.App-logo {
@ -14,7 +17,6 @@
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
@ -41,6 +43,8 @@
text-align: left;
padding: 1em;
border: 1px solid black;
width: 100%;
}
.Page-contents pre {
width: 100%;
}

View File

@ -4,7 +4,7 @@ import './App.css';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useParams, useNavigate } from 'react-router-dom';
import { useState } from 'react';
import { apiUrl, fetchPage, postPage, deletePage } from './clientStuff.jsx';
import { apiUrl, fetchPage, postPage, deletePage } from './apiTools.jsx';
function PageView() {
const queryClient = useQueryClient();
@ -56,43 +56,42 @@ function PageView() {
navigate(`/`);
}
return (
<div className="App">
<header className="App-header">
<a href="/"><img src={logo} className="App-logo" alt="logo" /></a>
<div className="Page-title">
<h3>
{pagenumber}.
<span
contentEditable="true"
dangerouslySetInnerHTML={{__html: ready ? page_title : "..." }} />
</h3>
</div>
{ ready ?
<>
<div className="Page-contents">
<pre
contentEditable="true"
dangerouslySetInnerHTML={{__html: page_text}} />
</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>
</>
:
<section className="App-header">
<a href="/"><img src={logo} className="App-logo" alt="logo" /></a>
<div className="Page-title">
<h3>
{pagenumber}.
<span
contentEditable="true"
dangerouslySetInnerHTML={{__html: ready ? page_title : "..." }} />
</h3>
</div>
{ ready ?
<>
<div className="Page-contents">
{ fetchQuery.isPending ? "Loading..." : JSON.stringify(fetchQuery.error) }
<pre
contentEditable="true"
dangerouslySetInnerHTML={{__html: page_text}} />
</div>
}
</header>
<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>
</div>
);
}

View File

@ -1,14 +1,16 @@
import logo from './logo.svg';
import './App.css';
import { useNavigate } from 'react-router-dom'
import { useNavigate, Link } from 'react-router-dom'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { apiUrl, fetchPageList, postNewPage } from './clientStuff.jsx';
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
@ -38,8 +40,8 @@ function Landing() {
<ul>
{
data.map((row) =>
<li>
<a key={row} href={`/${row}`}>page {row}</a>
<li key={row}>
<a href={`/${row}`} {...noLoad}>page {row}</a>
</li>
)
}

View File

@ -4,11 +4,14 @@ import './App.css';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useParams, useNavigate } from 'react-router-dom';
import { useState } from 'react';
import { apiUrl, fetchPage, postPage } from './clientStuff.jsx';
import { apiUrl, fetchPage, postPage } from './apiTools.jsx';
import { useFixLinks } from './clientStuff.jsx';
function PageView() {
const queryClient = useQueryClient();
const navigate = useNavigate();
const noLoad = useFixLinks();
const { pagenumber } = useParams();
const { isPending, error, data } = useQuery({ // fetch the currrent values
@ -24,7 +27,7 @@ function PageView() {
return (
<div className="App">
<header className="App-header">
<a href="/"><img src={logo} className="App-logo" alt="logo" /></a>
<a href="/" {...noLoad}><img src={logo} className="App-logo" alt="logo" /></a>
<div className="Page-title">
<h3>{pagenumber}. {ready ? (page_title || " ") : "..."}</h3>
</div>
@ -32,6 +35,7 @@ function PageView() {
<div
className="Page-contents"
dangerouslySetInnerHTML={{ __html: (page_html || " ") }}
{...noLoad}
/>
:
<div className="Page-contents">

45
client/src/apiTools.jsx Normal file
View File

@ -0,0 +1,45 @@
// This is wrapper functtions to do requests to the api, from the frontend.
export const apiUrl = "http://127.0.0.1:3001"
const defaults = {
headers: {'Content-Type': 'application/json'}
};
export async function postNewPage() {
return fetch(`${apiUrl}/page/new`, {
method: 'POST',
...defaults
}).then((res) => res.json())
.then((data) => data.id)
}
export async function fetchPageList() {
return fetch(`${apiUrl}/pages`, {
method: 'GET',
...defaults
}).then((res) => res.json())
}
export async function fetchPage(id) {
return fetch(`${apiUrl}/page/${id}`, {
method: 'GET',
...defaults
}).then((res) => res.json())
}
export async function postPage({id, title, description}) {
return fetch(`${apiUrl}/page/${id}`, {
method: 'POST',
body: JSON.stringify({id: id, title: title, description: description}),
...defaults
})
}
export async function deletePage(id) {
return fetch(`${apiUrl}/page/${id}`, {
method: 'DELETE',
headers: {'Content-Type': 'application/json'},
...defaults
})
}

View File

@ -1,43 +1,14 @@
export const apiUrl = "http://127.0.0.1:3001"
// New hook to make links use the react-router `navigate` method instead of default browser behavior.
const defaults = {
headers: {'Content-Type': 'application/json'}
};
import { useNavigate } from 'react-router-dom'
export async function postNewPage() {
return fetch(`${apiUrl}/page/new`, {
method: 'POST',
...defaults
}).then((res) => res.json())
.then((data) => data.id)
}
export async function fetchPageList() {
return fetch(`${apiUrl}/pages`, {
method: 'GET',
...defaults
}).then((res) => res.json())
}
export async function fetchPage(id) {
return fetch(`${apiUrl}/page/${id}`, {
method: 'GET',
...defaults
}).then((res) => res.json())
}
export async function postPage({id, title, description}) {
return fetch(`${apiUrl}/page/${id}`, {
method: 'POST',
body: JSON.stringify({id: id, title: title, description: description}),
...defaults
})
}
export async function deletePage(id) {
return fetch(`${apiUrl}/page/${id}`, {
method: 'DELETE',
headers: {'Content-Type': 'application/json'},
...defaults
})
export function useFixLinks() {
// spread this return value on <a/> elements in order to make them navigate
const navigate = useNavigate();
return { onClick: (e) => {
if (e.target.tagName != "A") { return; }
if (!e.target.href.includes(window.location.origin)) { return; }
e.preventDefault();
navigate(e.target.href.replace(window.location.origin, ""));
}};
}

View File

@ -1 +0,0 @@
'{"statements": [{"q": ["replace into pages (id, title, description) values (?, ?, ?)", "params": [2,"main2","dummyvalue"] }], }'

View File

@ -1 +1 @@
{"id":"7de13931-444c-4dd2-a19a-5cd1e70b7f36","rows_written":109,"rows_read":1706,"storage_bytes_used":12288,"write_requests_delegated":0,"current_frame_no":108,"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 id, title, description from pages where id=@val","rows_written":0,"rows_read":1},{"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":"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":877,"query_latency":90259}
{"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}

Binary file not shown.

19
server/dbHelper.js Normal file
View File

@ -0,0 +1,19 @@
// helper function(s?) to make a query to the database
const db_url = 'http://localhost:8000'
async function query(q, params) {
return fetch(db_url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
statements: [{
q: q,
params: params
}]
})
}).then((res) => res.json())
.then((data) => data[0].results);
}
module.exports = { query: query };

View File

@ -1,7 +1,7 @@
const express = require('express');
const showdown = require('showdown');
const { query } = require('./dbHelper.js');
const db_url = 'http://localhost:8000'
const port = process.env.PORT || 3001; // Use the port provided by the host or default to 3000
const app = express();
@ -19,93 +19,41 @@ app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
// Define a route to handle incoming requests
app.post('/', (req, res) => {
res.send('Hello, Express!');
});
app.get('/pages', (req, res) => {
fetch(db_url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
statements: ['select id from pages']
})
}).then((rsp) => rsp.json())
.then((data) => data[0].results.rows)
.then((rows) => rows.map(([id]) => id))
.then((ids) => res.json(ids))
query('select id from pages', [])
.then((r) => r.rows)
.then((row) => row.map(([id]) => id))
.then((ids) => res.status(200).json(ids))
.catch((error) => res.status(500).json({"error": error}));
});
app.post('/page/new', (req, res) => {
fetch(db_url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
statements: [{
q: 'insert into pages (title, description) values (?, ?) returning id',
params: ["new page", "this page is new!"]
}]
})
}).then((rsp) => rsp.json())
.then((data) => data[0].results.rows[0][0])
query('insert into pages (title, description) values (?, ?) returning id',
["new page", "this page is new!"])
.then((r) => r.rows[0][0])
.then((row) => res.status(200).json({id: row}))
.catch((error) => res.status(500).json({"error": error}))
});
app.get('/page/:id', (req, res) => {
fetch(db_url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
statements: [{
q: 'select * from pages where id=?',
params: [req.params.id]
}]
})
}).then((rsp) => rsp.json())
.then((data) => {
if (data[0].results.rows.length == 0) return res.status(404).json({"error": "page not found in db"});
return data[0].results.rows[0];
query('select * from pages where id=?', [req.params.id])
.then((r) => {
if (r.rows.length == 0) return res.status(404).json({"error": "page not found in db"});
return r.rows[0];
}).then((row) => res.status(200).json(row))
.catch((error) => res.status(500).json({"error": error}));
});
app.post('/page/:id', (req, res) => {
console.log(`got a post for /page/${req.params.id}`, req.body);
console.log(`rendering new input ${req.params.id}`);
const html = converter.makeHtml(req.body.description);
console.log(`the output was ${html}`)
console.log("done rendering");
fetch(db_url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
statements: [{
q: 'replace into pages (id, title, description, html) values (?, ?, ?, ?)',
params: [Number(req.params.id), req.body.title, req.body.description, html],
}]
})
}).then(() => res.status(200).json({}))
query('replace into pages (id, title, description, html) values (?, ?, ?, ?)',
[req.params.id, req.body.title, req.body.description, html])
.then(() => res.status(200).json({}))
.catch((error) => res.status(500).json({"error": error}));
});
app.delete('/page/:id', (req, res) => {
console.log(`deleting page #${req.params.id}`);
fetch(db_url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
statements: [{
q: 'delete from pages where id = ?',
params: [Number(req.params.id)]
}]
})
}).then(() => res.status(200).json({id: req.params.id}))
query('delete from pages where id = ?', [req.params.id])
.then(() => res.status(200).json({id: req.params.id}))
.catch((error) => res.status(500).json({"error": error}))
})