moving to server/db sameness and express-session
This commit is contained in:
parent
ee5402ab44
commit
32913d582a
16
client/package-lock.json
generated
16
client/package-lock.json
generated
@ -20,6 +20,7 @@
|
|||||||
"graphology-layout-force": "^0.2.4",
|
"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-hook-form": "^7.53.0",
|
||||||
"react-router-dom": "^6.26.2",
|
"react-router-dom": "^6.26.2",
|
||||||
"react-sigma": "^1.2.35",
|
"react-sigma": "^1.2.35",
|
||||||
"sigma": "^3.0.0-beta.29",
|
"sigma": "^3.0.0-beta.29",
|
||||||
@ -3088,6 +3089,21 @@
|
|||||||
"react": "^18.3.1"
|
"react": "^18.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-hook-form": {
|
||||||
|
"version": "7.53.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.0.tgz",
|
||||||
|
"integrity": "sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/react-hook-form"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17 || ^18 || ^19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "17.0.2",
|
"version": "17.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
"graphology-layout-force": "^0.2.4",
|
"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-hook-form": "^7.53.0",
|
||||||
"react-router-dom": "^6.26.2",
|
"react-router-dom": "^6.26.2",
|
||||||
"react-sigma": "^1.2.35",
|
"react-sigma": "^1.2.35",
|
||||||
"sigma": "^3.0.0-beta.29",
|
"sigma": "^3.0.0-beta.29",
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
||||||
import Landing from '/src/landing/Landing.jsx'
|
import Landing from '/src/landing/Landing.jsx';
|
||||||
import PageView from '/src/page/PageView.jsx'
|
import PageView from '/src/page/PageView.jsx';
|
||||||
import PageEdit from '/src/page/PageEdit.jsx'
|
import PageEdit from '/src/page/PageEdit.jsx';
|
||||||
|
import LogIn from '/src/login/LogIn.jsx';
|
||||||
|
import Register from '/src/login/Register.jsx';
|
||||||
|
|
||||||
import './App.css'
|
import './App.css';
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
@ -14,6 +16,8 @@ function App() {
|
|||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Landing/>}/>
|
<Route path="/" element={<Landing/>}/>
|
||||||
|
<Route path="/login" element={<LogIn/>}/>
|
||||||
|
<Route path="/register" element={<Register/>}/>
|
||||||
<Route path="/:pagenumber" element={<PageView/>}/>
|
<Route path="/:pagenumber" element={<PageView/>}/>
|
||||||
<Route path="/:pagenumber/edit" element={<PageEdit/>}/>
|
<Route path="/:pagenumber/edit" element={<PageEdit/>}/>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
@ -4,8 +4,19 @@ import Graph from 'graphology';
|
|||||||
|
|
||||||
export const apiUrl = `${window.location.origin}/api`;
|
export const apiUrl = `${window.location.origin}/api`;
|
||||||
|
|
||||||
|
|
||||||
|
//lil helper to throw errorrs from the promise when we get not-ok results
|
||||||
|
export const shoofetch = (url, config) => fetch(url, {...config, ...defaults})
|
||||||
|
.then(async (res) => {
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`got an error from the server: ${await res.text()}`);
|
||||||
|
}
|
||||||
|
return res.json();
|
||||||
|
});
|
||||||
|
|
||||||
const defaults = {
|
const defaults = {
|
||||||
headers: {'Content-Type': 'application/json'}
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
credentials: 'include'
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function postNewPage() {
|
export async function postNewPage() {
|
||||||
@ -41,7 +52,6 @@ export async function postPage({id, title, description}) {
|
|||||||
export async function deletePage(id) {
|
export async function deletePage(id) {
|
||||||
return fetch(`${apiUrl}/page/${id}`, {
|
return fetch(`${apiUrl}/page/${id}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {'Content-Type': 'application/json'},
|
|
||||||
...defaults
|
...defaults
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -57,3 +67,21 @@ export async function fetchGraph() {
|
|||||||
return graph;
|
return graph;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createAccount({name, password, nonce}) {
|
||||||
|
return shoofetch(`${apiUrl}/register`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({name: name, password: password, nonce: nonce})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function logIn({name, password}) {
|
||||||
|
return shoofetch(`${apiUrl}/login`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({name: name, password: password})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function logOut() {
|
||||||
|
return shoofetch(`${apiUrl}/logout`, { method: 'POST' });
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import App from './App.jsx';
|
import App from './App.jsx';
|
||||||
import reportWebVitals from './reportWebVitals';
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
|
|
||||||
@ -10,8 +9,3 @@ root.render(
|
|||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
|
||||||
// to log results (for example: reportWebVitals(console.log))
|
|
||||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
|
||||||
reportWebVitals();
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
.landing-page {
|
.landing-page {
|
||||||
padding-left:10rem;
|
width: 76ch;
|
||||||
padding-right: 10rem;
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
padding-top: 3rem;
|
padding-top: 3rem;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useNavigate, Link } from 'react-router-dom'
|
import { useNavigate, Link } from 'react-router-dom'
|
||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { fetchPageList, postNewPage } from '../apiTools.jsx';
|
import { fetchPageList, postNewPage, logOut } from '../apiTools.jsx';
|
||||||
import { useFixLinks } from '../clientStuff.jsx';
|
import { useFixLinks } from '../clientStuff.jsx';
|
||||||
|
|
||||||
import PageList from './PageList.jsx';
|
import PageList from './PageList.jsx';
|
||||||
@ -37,9 +37,10 @@ function Landing() {
|
|||||||
<PageList />
|
<PageList />
|
||||||
</section>
|
</section>
|
||||||
<section className="landing-section">
|
<section className="landing-section">
|
||||||
<button>Sign up</button><br/>
|
<button onClick={() => navigate('/register')}>Sign up</button><br/>
|
||||||
<button>Log in</button><br/>
|
<button onClick={() => navigate('/login')}>Log in</button><br/>
|
||||||
<button onClick={makeNewPage.mutate} >Dig new room!</button><br/>
|
<button onClick={makeNewPage.mutate}>Dig new room!</button><br/>
|
||||||
|
<button onClick={logOut}>Log Out</button><br/>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1 +1,32 @@
|
|||||||
LogIn.jsx
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { logIn } from '../apiTools.jsx';
|
||||||
|
|
||||||
|
function LogIn() {
|
||||||
|
const { register, handleSubmit } = useForm();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const onSubmit = (d) => {
|
||||||
|
logIn(d)
|
||||||
|
.then(() => navigate('/'))
|
||||||
|
.catch((error) => console.log('error logging in: ', error));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<label>
|
||||||
|
User name:
|
||||||
|
<input {...register("name")} />
|
||||||
|
<br/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Password:
|
||||||
|
<input {...register("password")} />
|
||||||
|
<br/>
|
||||||
|
</label>
|
||||||
|
<button type="submit">Log right in, buckaroo!</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogIn;
|
37
client/src/login/Register.jsx
Normal file
37
client/src/login/Register.jsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { createAccount } from "../apiTools.jsx";
|
||||||
|
|
||||||
|
function Register() {
|
||||||
|
const { register, handleSubmit } = useForm();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const onSubmit = (d) => {
|
||||||
|
createAccount(d)
|
||||||
|
.then(() => navigate("/login"))
|
||||||
|
.catch((error) => console.log("error: ", error));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<label>
|
||||||
|
User name:
|
||||||
|
<input {...register("name")} />
|
||||||
|
<br/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Password:
|
||||||
|
<input {...register("password")} />
|
||||||
|
<br/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Nonce password from shoofle:
|
||||||
|
<input {...register("nonce")} />
|
||||||
|
<br/>
|
||||||
|
</label>
|
||||||
|
<button type="submit">Request Account!</button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Register;
|
@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
.page-container {
|
.page-container {
|
||||||
padding-left:10rem;
|
width: 76ch;
|
||||||
padding-right: 10rem;
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
padding-top: 3rem;
|
padding-top: 3rem;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
const reportWebVitals = onPerfEntry => {
|
|
||||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
|
||||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
|
||||||
getCLS(onPerfEntry);
|
|
||||||
getFID(onPerfEntry);
|
|
||||||
getFCP(onPerfEntry);
|
|
||||||
getLCP(onPerfEntry);
|
|
||||||
getTTFB(onPerfEntry);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default reportWebVitals;
|
|
@ -10,6 +10,18 @@ create table if not exists pages (
|
|||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const createUsers = `
|
||||||
|
create table if not exists users (
|
||||||
|
name varchar(64) primary key,
|
||||||
|
password varchar(128)
|
||||||
|
)
|
||||||
|
`;
|
||||||
|
|
||||||
|
const createTokens = `
|
||||||
|
create table if not exists tokens (
|
||||||
|
)
|
||||||
|
`;
|
||||||
|
|
||||||
async function initDb() {
|
async function initDb() {
|
||||||
const config = {
|
const config = {
|
||||||
url: "http://127.0.0.1:8000"
|
url: "http://127.0.0.1:8000"
|
||||||
@ -19,6 +31,7 @@ async function initDb() {
|
|||||||
|
|
||||||
const converter = new showdown.Converter();
|
const converter = new showdown.Converter();
|
||||||
|
|
||||||
|
console.log("creating page table")
|
||||||
console.log(await db.execute(createPages));
|
console.log(await db.execute(createPages));
|
||||||
|
|
||||||
console.log("finding pages that havent been rendered");
|
console.log("finding pages that havent been rendered");
|
||||||
@ -35,7 +48,11 @@ async function initDb() {
|
|||||||
sql: `replace into pages (id, title, description, html) values (?, ?, ?, ?)`,
|
sql: `replace into pages (id, title, description, html) values (?, ?, ?, ?)`,
|
||||||
args: [id, title, description, renderedPage]
|
args: [id, title, description, renderedPage]
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("creating user table");
|
||||||
|
console.log(await db.execute(createUsers));
|
||||||
}
|
}
|
||||||
|
|
||||||
initDb();
|
initDb();
|
128
server/api.js
128
server/api.js
@ -1,72 +1,122 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const showdown = require('showdown');
|
const showdown = require('showdown');
|
||||||
|
const argon2 = require('argon2');
|
||||||
|
|
||||||
const { query } = require('./dbHelper.js');
|
const { query } = require('./dbHelper.js');
|
||||||
const { graphFromList } = require('./graphStuff.js');
|
const { graphFromList } = require('./graphStuff.js');
|
||||||
|
|
||||||
const app = express.Router();
|
const app = express.Router();
|
||||||
const converter = new showdown.Converter();
|
const converter = new showdown.Converter();
|
||||||
|
|
||||||
|
const sqlite = require("better-sqlite3");
|
||||||
|
const db = new sqlite('the_big_db.db', { verbose: console.log });
|
||||||
|
console.log(db);
|
||||||
|
|
||||||
app.get('/pages', (req, res) => {
|
app.get('/pages', (req, res) => {
|
||||||
query('select id, title from pages', [])
|
try {
|
||||||
.then((r) => r.rows)
|
const pages = db.prepare('select id, title from pages').all();
|
||||||
.then((row) => row.map(([id, title]) => {return {"id": id, "title": title};}))
|
res.status(200).json(pages);
|
||||||
.then((pages) => res.status(200).json(pages))
|
} catch (error) {
|
||||||
.catch((error) => res.status(500).json({"error": 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',
|
try {
|
||||||
["new page", "this page is new!"])
|
const newPage = db.prepare('insert into pages (title, description) values (?, ?) returning id')
|
||||||
.then((r) => r.rows[0])
|
.get("new page", "this page is new!");
|
||||||
.then((row) => res.status(200).json({id: row[0]}))
|
res.status(200).json(newPage);
|
||||||
.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])
|
try {
|
||||||
.then((r) => {
|
const page = db.prepare('select * from pages where id=:id').get(req.params);
|
||||||
if (r.rows.length == 0) res.status(404).json({"error": "page not found in db"});
|
if (page === undefined) res.status(404).json({"error": "page not found"});
|
||||||
else return r.rows[0];
|
else res.status(200).json(page);
|
||||||
}).then((row) => res.status(200).json(row))
|
} catch (error) {
|
||||||
.catch((error) => res.status(500).json({"error": error}));
|
res.status(500).json({"error": error});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/page/:id', (req, res) => {
|
app.post('/page/:id', (req, res) => {
|
||||||
const html = converter.makeHtml(req.body.description);
|
try {
|
||||||
query('replace into pages (id, title, description, html) values (?, ?, ?, ?)',
|
const html = converter.makeHtml(req.body.description);
|
||||||
[req.params.id, req.body.title, req.body.description, html])
|
const changes = db.prepare('replace into pages (id, title, descriptioon, html) values (?, ?, ?, ?)')
|
||||||
.then(() => res.status(200).json({}))
|
.run(req.params.id, req.body.title, req.body.description, html);
|
||||||
.catch((error) => res.status(500).json({"error": error}));
|
res.status(200).json(changes);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({"error": error});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete('/page/:id', (req, res) => {
|
app.delete('/page/:id', (req, res) => {
|
||||||
query('delete from pages where id = ?', [req.params.id])
|
try {
|
||||||
.then(() => res.status(200).json({id: req.params.id}))
|
const changes = db.prepare('delete from pages where id = ?').run(req.params.id);
|
||||||
.catch((error) => res.status(500).json({"error": error}))
|
res.status(200).json({id: req.params.id});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({"error": error});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/graph', (req, res) => {
|
app.get('/graph', (req, res) => {
|
||||||
query('select id, html from pages', [])
|
try {
|
||||||
.then((r) => r.rows)
|
const rows = db.prepare('select id, html from pages').all();
|
||||||
.then((stuff) => graphFromList(stuff))
|
const graph = graphFromList(rows);
|
||||||
.then((graph) => res.json(graph))
|
res.status(200).json(graph);
|
||||||
.catch((error) => res.status(500).json({"error": error}));
|
} catch (error) {
|
||||||
|
res.status(500).json({"error": error});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// auth stuff
|
// auth stuff
|
||||||
app.post('/register', (req, res) => {
|
app.post('/register', async (req, res) => {
|
||||||
// parse the body, which miight be url-encoded?
|
const {name, password, nonce} = req.body;
|
||||||
|
|
||||||
|
const oldUser = db.prepare('select name from users where name=?').get(name);
|
||||||
|
if (oldUser) return res.status(500).json({"error": "user name already in use"});
|
||||||
|
|
||||||
// check if the nonce password is correctt
|
// check if the nonce password is correctt
|
||||||
// hash the password
|
if (nonce != "a softer birdsong") return res.status(500).json({"error": "wrong nonce"});
|
||||||
// add tthe user to the users database
|
|
||||||
|
try {
|
||||||
|
// i'm told argon2 is the good one nowatimes
|
||||||
|
const hash = await argon2.hash(password);
|
||||||
|
const inserted = db.prepare('insert into users (name, password) values (?, ?)').run(name, hash);
|
||||||
|
res.status(200).json(inserted);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({"error": error});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/login', (req, res) => {
|
app.post('/login', async (req, res) => {
|
||||||
// hash the password
|
if (req.session.name) {
|
||||||
// check if the user/hashed pass matches
|
return res.status(200).json({message: "already logged in", name: req.session.name});
|
||||||
// if not, throw an error
|
}
|
||||||
// create a valid ttoken?
|
|
||||||
// return token
|
const {name, password} = req.body;
|
||||||
|
|
||||||
|
// fetch username and passswords from the db
|
||||||
|
const storedUser = db.prepare('select name, password from users where name = ?').get(name);
|
||||||
|
if (!storedUser) {
|
||||||
|
return res.status(401).json({"error": "password/username combo not found in database"});
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if the passss hashes mattch and log in
|
||||||
|
if (!(await argon2.verify(storedUser.password, password))) {
|
||||||
|
return res.status(401).json({"error": "password/username combo not found in database"});
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the session cookie and rreturn 200!
|
||||||
|
req.session.name = name;
|
||||||
|
return res.status(200).json({message: "successfully logged in!", name: name});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/logout', (req, res) => {
|
||||||
|
req.session.destroy();
|
||||||
|
res.status(200).json({message: "successfully logged out"});
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = app;
|
module.exports = app;
|
@ -1,19 +0,0 @@
|
|||||||
// helper function(s?) to make a query to the database
|
|
||||||
|
|
||||||
const db_url = 'http://127.0.0.1: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 };
|
|
31
server/db_scripts/copy_db.js
Normal file
31
server/db_scripts/copy_db.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
const sqlite = require("better-sqlite3");
|
||||||
|
const sqlite3_db = new sqlite('./the_big_db.db', { verbose: console.log });
|
||||||
|
|
||||||
|
const libsql = require("@libsql/client");
|
||||||
|
const config = {url: "http://127.0.0.1:8000"};
|
||||||
|
const libsql_db = libsql.createClient(config);
|
||||||
|
|
||||||
|
async function copyPages() {
|
||||||
|
console.log("copying everythting from hte old db");
|
||||||
|
const pages = (await libsql_db.execute(`select id, title, description, html from pages`)).rows;
|
||||||
|
|
||||||
|
const insertPage = sqlite3_db.prepare(`replace into pages (id, title, description, html) values (:id, :title, :description, :html)`);
|
||||||
|
pages.forEach((page) => {
|
||||||
|
console.log(`inserting page ${page.id}`)
|
||||||
|
insertPage.run(page);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyUsers() {
|
||||||
|
console.log("copying users now");
|
||||||
|
const users = (await libsql_db.execute(`select name, password from users`)).rows;
|
||||||
|
|
||||||
|
const insertUser = sqlite3_db.prepare(`replace into users (name, password) values (:name, :password)`);
|
||||||
|
users.forEach((user) => {
|
||||||
|
console.log(`inserting ${user.name}, ${user.password}`);
|
||||||
|
insertUser.run(user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
copyPages();
|
||||||
|
copyUsers();
|
48
server/db_scripts/initialize_db.js
Normal file
48
server/db_scripts/initialize_db.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
const sqlite = require("better-sqlite3");
|
||||||
|
const showdown = require("showdown");
|
||||||
|
const db = new sqlite('./the_big_db.db', { verbose: console.log });
|
||||||
|
|
||||||
|
const converter = new showdown.Converter();
|
||||||
|
|
||||||
|
const createPages = db.prepare(`
|
||||||
|
create table if not exists pages (
|
||||||
|
id integer primary key,
|
||||||
|
title varchar(255),
|
||||||
|
description text,
|
||||||
|
html text
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
|
||||||
|
const createUsers = db.prepare(`
|
||||||
|
create table if not exists users (
|
||||||
|
name varchar(64) primary key,
|
||||||
|
password varchar(128)
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
|
||||||
|
|
||||||
|
function initDb() {
|
||||||
|
console.log("creating page table")
|
||||||
|
console.log(createPages.run());
|
||||||
|
|
||||||
|
console.log("finding pages that havent been rendered");
|
||||||
|
rows = db.prepare(`select * from pages where html is null`).all();
|
||||||
|
console.log(rows);
|
||||||
|
|
||||||
|
const insertPage = db.prepare(`replace into pages (id, title, description, html) values (:id, :title, :description, :html)`);
|
||||||
|
|
||||||
|
rows.forEach((pageData) => {
|
||||||
|
const {id, title, description, html} = pageData;
|
||||||
|
console.log(`rendering page number ${id}`)
|
||||||
|
console.log(`${id}. ${title}: ${description} ( ${html} )`);
|
||||||
|
|
||||||
|
const renderedPage = converter.makeHtml(description);
|
||||||
|
|
||||||
|
insertPage.run({...pageData, html: renderedPage});
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("creating user table");
|
||||||
|
console.log(createUsers.run());
|
||||||
|
}
|
||||||
|
|
||||||
|
initDb();
|
4
server/db_scripts/list_tables.js
Normal file
4
server/db_scripts/list_tables.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
const sqlite = require("better-sqlite3");
|
||||||
|
const db = new sqlite('./the_big_db.db', { verbose: console.log });
|
||||||
|
|
||||||
|
console.log(db.prepare("select name from sqlite_master where type='table'").all());
|
@ -4,12 +4,12 @@ const { circular } = require('graphology-layout');
|
|||||||
|
|
||||||
function graphFromList(allTheStuff) {
|
function graphFromList(allTheStuff) {
|
||||||
const graph = new graphology.Graph();
|
const graph = new graphology.Graph();
|
||||||
|
for (const {id, html} of allTheStuff) {
|
||||||
|
|
||||||
for (const [id, html] of allTheStuff) {
|
|
||||||
graph.addNode(id);
|
graph.addNode(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [id, html] of allTheStuff) {
|
for (const {id, html} of allTheStuff) {
|
||||||
const { document } = (new JSDOM(html)).window;
|
const { document } = (new JSDOM(html)).window;
|
||||||
const links = document.querySelectorAll('a');
|
const links = document.querySelectorAll('a');
|
||||||
links.forEach((link) => {
|
links.forEach((link) => {
|
||||||
|
818
server/package-lock.json
generated
818
server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,13 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@libsql/client": "^0.14.0",
|
||||||
|
"argon2": "^0.41.1",
|
||||||
|
"better-sqlite3": "^11.3.0",
|
||||||
|
"better-sqlite3-session-store": "^0.1.0",
|
||||||
|
"client-sessions": "^0.8.0",
|
||||||
"express": "^4.21.0",
|
"express": "^4.21.0",
|
||||||
|
"express-session": "^1.18.0",
|
||||||
"graphology": "^0.25.4",
|
"graphology": "^0.25.4",
|
||||||
"graphology-layout": "^0.6.1",
|
"graphology-layout": "^0.6.1",
|
||||||
"graphology-layout-force": "^0.2.4",
|
"graphology-layout-force": "^0.2.4",
|
||||||
|
@ -1,11 +1,28 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
const session = require('express-session');
|
||||||
|
const sqlite = require('better-sqlite3');
|
||||||
|
|
||||||
const apiRoutes = require('./api.js');
|
const apiRoutes = require('./api.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
|
||||||
|
const db = new sqlite('the_big_db.db', { verbose: console.log });
|
||||||
|
const SqliteStore = require('better-sqlite3-session-store')(session);
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
|
app.use(session({
|
||||||
|
store: new SqliteStore({
|
||||||
|
client: db,
|
||||||
|
expired: {
|
||||||
|
clear: true,
|
||||||
|
intervalMs: 15*60*1000
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
secret: "dno'tt check me into versino control",
|
||||||
|
resave: false
|
||||||
|
}));
|
||||||
|
|
||||||
app.use("/api", apiRoutes);
|
app.use("/api", apiRoutes);
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user