added login

This commit is contained in:
didier amyot committed 2023-12-11 11:59:30 -05:00
1 parent 171657d616
commit 0cb2d25a62
3 files changed
+137 -26

No files matched your search

+29 -2
View File
@@ -12,17 +12,44 @@
<nav class="navbar" role="navigation" aria-label="main navigation"> <nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-menu"> <div class="navbar-menu">
<div class="navbar-start"> <div class="navbar-start">
<a class="navbar-item" href="https://bulma.io"> <a class="navbar-item" href="#" hx-post="/api/v1/logout">
<img src="images/js_logo.svg" alt="js logo" style="height: 100%"> <img src="images/js_logo.svg" alt="js logo" style="height: 100%">
<h2 class="title is-3 ml-1">Reading Exercices</h2> <h2 class="title is-3 ml-1">Reading Exercices</h2>
</a> </a>
</div> </div>
</div> </div>
</nav> </nav>
<div hx-get="/api/v1/random-questions/dsdsddsf/4" <div hx-get="/api/v1/random-questions/:seed/:defaultIndex"
hx-trigger="load" hx-trigger="load"
id="question-body"> id="question-body">
</div> </div>
<script>
function getDefaultIndex(){
if(document.location.hash.length > 1){
return document.location.hash.substr(1)
}
return localStorage.getItem("defaultIndex") || '0';
}
document.body.addEventListener('htmx:configRequest', function(evt) {
evt.detail.headers.session = localStorage.getItem("seed");
evt.detail.path = evt.detail.path.replace(":seed", localStorage.getItem("seed"));
evt.detail.path = evt.detail.path.replace(":defaultIndex", getDefaultIndex());
});
document.body.addEventListener('logout', function (evt) {
window.localStorage.clear();
document.location.href = "/login.html";
});
document.body.addEventListener('set-question-id', function (evt) {
if(isNaN(evt.detail.value)){
return;
}
document.location.hash = evt.detail.value
localStorage.setItem("defaultIndex", evt.detail.value);
});
</script>
</body> </body>
</html> </html>
+49
View File
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>JS Reading App</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<script src="https://unpkg.com/htmx.org@1.9.9"></script>
</head>
<body>
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" href="#">
<img src="images/js_logo.svg" alt="js logo" style="height: 100%">
<h2 class="title is-3 ml-1">Reading Exercices</h2>
</a>
</div>
</div>
</nav>
<div class="columns is-centered">
<div class="column is-half">
<form hx-post="/api/v1/login" hx-swap="none">
<div class="field has-addons">
<div class="control">
<input class="input" id="username" type="text" placeholder="bob.l.eponge@ocean.com" name="username" required>
</div>
<div class="control">
<input type="submit" class="button" value="Login">
</a>
</div>
</div>
</form>
</div>
</div>
<script>
document.body.addEventListener('set-seed', function (evt) {
const seed = document.getElementById("username").value;
window.localStorage.setItem("seed", seed);
document.location.href = "/";
});
</script>
</body>
</html>
+59 -24
View File
@@ -8,10 +8,16 @@ import {QuestionSource, QuestionMeta} from './shared.ts';
type Question = [QuestionSource, QuestionMeta]; type Question = [QuestionSource, QuestionMeta];
type QuestionURLS = {
nextQuestionUrl: string,
validationUrl: string,
answerQuestionUrl: string
};
const questions: Question = Object.values(rawQuestions).flatMap(s => s.questions.map( q => [s, q])); const questions: Question = Object.values(rawQuestions).flatMap(s => s.questions.map( q => [s, q]));
const router = new Router(); const apiRouter = new Router();
const loginRouter = new Router();
const app = new Application(); const app = new Application();
function hashString(str: string): number { function hashString(str: string): number {
@@ -45,7 +51,7 @@ function getRandomQuestion<T>(seed: string, index: number, questions: Question[
} }
function getQuestionPage(question: Question, nextQuestionUrl: string, validationUrl: string, answerQuestionUrl: string){ function getQuestionPage(question: Question, urls: QuestionURLS){
return (<div> return (<div>
<pre> { question[0].toString() }</pre> <pre> { question[0].toString() }</pre>
<div class="content mt-2 mb-2"> <div class="content mt-2 mb-2">
@@ -64,15 +70,15 @@ function getQuestionPage(question: Question, nextQuestionUrl: string, validation
<button class="button is-info" <button class="button is-info"
hx-trigger="click" hx-trigger="click"
hx-target="#message" hx-target="#message"
hx-post={validationUrl} > hx-post={urls.validationUrl} >
Verify Answer Verify Answer
</button> </button>
</div> </div>
<div class="control"> <div class="control">
<a class="button is-info" <a class="button is-info"
href="#" href="#"
hx-trigger="next-question from:body delay:1500ms, click" hx-trigger="next-question from:body delay:500ms, click"
hx-get={nextQuestionUrl} hx-get={urls.nextQuestionUrl}
hx-params="none" hx-params="none"
hx-target="#question-body" > hx-target="#question-body" >
Next Question Next Question
@@ -83,7 +89,7 @@ function getQuestionPage(question: Question, nextQuestionUrl: string, validation
<a class="button is-info" <a class="button is-info"
href="#" href="#"
hx-trigger="click" hx-trigger="click"
hx-get={answerQuestionUrl} hx-get={urls.answerQuestionUrl}
hx-params="none" hx-params="none"
hx-target="#message" > hx-target="#message" >
Show Answer Show Answer
@@ -120,43 +126,72 @@ function processValidAnswer(ctx, answer: string): void {
} }
router.all("/api/v1/random-questions/:seed/:idx/validate", async (ctx)=> { function getQuestion(ctx): Question{
const seed = ctx.params.seed;
const index = Number.parseInt(ctx.params.idx??"0");
return getRandomQuestion(seed, index, questions);
}
function getUrls(ctx): QuestionURLS{
const seed = ctx.params.seed; const seed = ctx.params.seed;
const index = Number.parseInt(ctx.params.idx??"0"); const index = Number.parseInt(ctx.params.idx??"0");
const answer = (await ctx.request.body().value).get("answer");
const nextQuestionUrl = `/api/v1/random-questions/${seed}/${index + 1}`; const nextQuestionUrl = `/api/v1/random-questions/${seed}/${index + 1}`;
const q = getRandomQuestion(seed, index, questions); const validationUrl = `/api/v1/random-questions/${seed}/${index}/validate`;
const answerQuestionUrl = `/api/v1/random-questions/${seed}/${index}/answer`
return {nextQuestionUrl, validationUrl, answerQuestionUrl};
}
apiRouter.post("/api/v1/random-questions/:seed/:idx/validate", async (ctx)=> {
const answer = (await ctx.request.body().value).get("answer");
const q = getQuestion(ctx);
if(isValidAnswer(q, answer)){ if(isValidAnswer(q, answer)){
processValidAnswer(ctx, nextQuestionUrl) processValidAnswer(ctx, getUrls(ctx).nextQuestionUrl)
}else{ }else{
processInvalidAnswer(ctx, nextQuestionUrl) processInvalidAnswer(ctx, getUrls(ctx).nextQuestionUrl)
} }
}) })
router.get("/api/v1/random-questions/:seed/:idx/answer", (ctx)=> { apiRouter.get("/api/v1/random-questions/:seed/:idx/answer", (ctx)=> {
const seed = ctx.params.seed; const q = getQuestion(ctx);
const index = Number.parseInt(ctx.params.idx??"0");
const q = getRandomQuestion(seed, index, questions);
ctx.response.body = renderToString( ctx.response.body = renderToString(
<div class="notification is-warning"> <div class="notification is-warning">
The answer is {q[1].answer} The answer is {q[1].answer}
</div>); </div>);
}) })
router.get("/api/v1/random-questions/:seed/:idx", (ctx)=> { apiRouter.get("/api/v1/random-questions/:seed/:idx", (ctx)=> {
const seed = ctx.params.seed; const q = getQuestion(ctx);
const index = Number.parseInt(ctx.params.idx??"0"); const urls = getUrls(ctx);
const q = getRandomQuestion(seed, index, questions);
const nextQuestionUrl = `/api/v1/random-questions/${seed}/${index + 1}`;
const validationUrl = `/api/v1/random-questions/${seed}/${index}/validate`;
const answerQuestionUrl = `/api/v1/random-questions/${seed}/${index}/answer`
ctx.response.body = renderToString(getQuestionPage(q, nextQuestionUrl, validationUrl, answerQuestionUrl)); ctx.response.body = renderToString(getQuestionPage(q, urls));
ctx.response.headers.append("HX-Trigger", JSON.stringify({'set-question-id': ctx.params.idx}));
}); });
app.use(router.routes()); loginRouter.post("/api/v1/login", async (ctx)=> {
const username = (await ctx.request.body().value).get("username");
ctx.response.headers.append("HX-Trigger", JSON.stringify({'set-seed': username}));
ctx.response.body = username;
});
loginRouter.post("/api/v1/logout", (ctx)=> {
ctx.response.headers.append("HX-Trigger", "logout");
ctx.response.body = "";
});
app.use(loginRouter.routes());
app.use(staticFiles("public", {brotli: true, fetch: true})); app.use(staticFiles("public", {brotli: true, fetch: true}));
app.use(async (ctx, next) => {
if(!ctx.request.headers.get('session')){
console.log("user not logged in.")
ctx.response.headers.append("HX-Redirect", "/login.html");
return
}
await next();
})
app.use(apiRouter.routes());
await app.listen({ port: 8000 }); await app.listen({ port: 8000 });