mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-08-02 03:55:23 +02:00
Typescript rewrite (#147)
* Updated highlight.js * Update .codexdocsrc.sample remove undefined page for a fresh new install * backend rewritten in TS * test -> TS, .added dockerignore, bug fixed * Removed compiled js files, eslint codex/ts added * fixed jsdocs warning, leaving editor confirmation * use path.resolve for DB paths * db drives updated + fixed User model * redundant cleared + style fixed * explicit type fixing * fixing testing code * added body block type * compiled JS files -> dist, fixed compiling errors * fixed compiling error, re-organized ts source code * updated Dockerfile * fixed link to parent page * up nodejs version * fix package name * fix deps Co-authored-by: nvc8996 <nvc.8996@gmail.com> Co-authored-by: Taly <vitalik7tv@yandex.ru>
This commit is contained in:
parent
059cfb96f9
commit
34514761f5
99 changed files with 3817 additions and 2249 deletions
16
src/backend/views/auth.twig
Normal file
16
src/backend/views/auth.twig
Normal file
|
@ -0,0 +1,16 @@
|
|||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<form class="auth-form" method="post" action="/auth">
|
||||
<h1>
|
||||
┬┴┬┴┤ ͜ʖ ͡°) ├┬┴┬┴
|
||||
</h1>
|
||||
<p>
|
||||
Enter a password to access pages editing
|
||||
</p>
|
||||
<input type="hidden" name="_csrf" value={{ csrfToken }}>
|
||||
<input type="password" name="password" placeholder="Password">
|
||||
<input type="submit" value="Login">
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
41
src/backend/views/components/aside.twig
Normal file
41
src/backend/views/components/aside.twig
Normal file
|
@ -0,0 +1,41 @@
|
|||
<div class="docs-aside-toggler" onclick="document.querySelector('.docs-aside').classList.toggle('docs-aside--toggled')">
|
||||
{{ svg('menu') }} Table of contents
|
||||
</div>
|
||||
<div class="docs-aside">
|
||||
{% for firstLevelPage in menu %}
|
||||
<section class="docs-aside__section">
|
||||
<a
|
||||
{% if page is defined and page._id == firstLevelPage._id%}
|
||||
class="docs-aside__section-title docs-aside__current"
|
||||
{% else %}
|
||||
class="docs-aside__section-title"
|
||||
{% endif %}
|
||||
{% if firstLevelPage.uri %}
|
||||
href="/{{ firstLevelPage.uri }}"
|
||||
{% else %}
|
||||
href="/page/{{ firstLevelPage._id }}"
|
||||
{% endif %}>
|
||||
{{ firstLevelPage.title | striptags }}
|
||||
</a>
|
||||
{% if firstLevelPage.children is not empty %}
|
||||
<ul class="docs-aside__section-list">
|
||||
{% for child in firstLevelPage.children %}
|
||||
<li>
|
||||
<a
|
||||
{% if page is defined and page._id == child._id %}
|
||||
class="docs-aside__current"
|
||||
{% endif %}
|
||||
{% if child.uri %}
|
||||
href="/{{ child.uri }}"
|
||||
{% else %}
|
||||
href="/page/{{ child._id }}"
|
||||
{% endif %}>
|
||||
{{ child.title | striptags }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
27
src/backend/views/components/header.twig
Normal file
27
src/backend/views/components/header.twig
Normal file
|
@ -0,0 +1,27 @@
|
|||
<header class="docs-header">
|
||||
<a href="/" class="docs-header__logo">
|
||||
{{ config.title | striptags }}
|
||||
</a>
|
||||
<ul class="docs-header__menu">
|
||||
{% if isAuthorized == true %}
|
||||
<li class="docs-header__menu-add">
|
||||
<a class="docs-header__button" href="/page/new">
|
||||
{{ svg('plus') }}
|
||||
Add Page
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for option in config.menu %}
|
||||
<li>
|
||||
<a
|
||||
{% if option.uri %}
|
||||
href="{{ option.uri }}"
|
||||
{% else %}
|
||||
href="/page/{{ option._id }}"
|
||||
{% endif %}>
|
||||
{{ option.title | striptags }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</header>
|
7
src/backend/views/error.twig
Normal file
7
src/backend/views/error.twig
Normal file
|
@ -0,0 +1,7 @@
|
|||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{message}}</h1>
|
||||
<h2>{{error.status}}</h2>
|
||||
<pre>{{error.stack}}</pre>
|
||||
{% endblock %}
|
45
src/backend/views/layout.twig
Normal file
45
src/backend/views/layout.twig
Normal file
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ (page.title ?: config.title) | striptags }}</title>
|
||||
<link rel="stylesheet" href="/dist/main.css" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:title" content="{{ page.title | striptags }}" />
|
||||
<meta property="article:modified_time" content="{{ (page.body.time / 1000) | date("c") }}" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
</head>
|
||||
<script>
|
||||
window.config = {
|
||||
misprintsChatId: "{{ config.misprintsChatId }}"
|
||||
};
|
||||
</script>
|
||||
<body>
|
||||
{% include "components/header.twig" with res.locals.isAuthorized %}
|
||||
<div class="docs">
|
||||
<aside class="docs__aside">
|
||||
{% include "components/aside.twig" %}
|
||||
</aside>
|
||||
<div class="docs__content">
|
||||
<div class="docs__content-inner">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/dist/main.bundle.js"></script>
|
||||
{% if config.yandexMetrikaId is not empty %}
|
||||
<script type="text/javascript" >
|
||||
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
||||
m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
|
||||
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
|
||||
|
||||
ym({{ config.yandexMetrikaId }}, "init", {
|
||||
clickmap:true,
|
||||
trackLinks:true,
|
||||
accurateTrackBounce:true
|
||||
});
|
||||
</script>
|
||||
<noscript><div><img src="https://mc.yandex.ru/watch/{{ config.yandexMetrikaId }}" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
12
src/backend/views/pages/blocks/checklist.twig
Normal file
12
src/backend/views/pages/blocks/checklist.twig
Normal file
|
@ -0,0 +1,12 @@
|
|||
<div class="block-checklist">
|
||||
{% for item in items %}
|
||||
<div class="block-checklist__item">
|
||||
{% if item.checked %}
|
||||
<span class="block-checklist__item-checkbox block-checklist__item-checkbox--checked"></span>
|
||||
{% else %}
|
||||
<span class="block-checklist__item-checkbox"></span>
|
||||
{% endif %}
|
||||
<div class="block-checklist__item-text">{{ item.text }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
4
src/backend/views/pages/blocks/code.twig
Normal file
4
src/backend/views/pages/blocks/code.twig
Normal file
|
@ -0,0 +1,4 @@
|
|||
<div class="block-code">
|
||||
<div class="block-code__content">{{ code|escape }}</div>
|
||||
</div>
|
||||
|
1
src/backend/views/pages/blocks/delimiter.twig
Normal file
1
src/backend/views/pages/blocks/delimiter.twig
Normal file
|
@ -0,0 +1 @@
|
|||
<div class="block-delimiter"></div>
|
12
src/backend/views/pages/blocks/embed.twig
Normal file
12
src/backend/views/pages/blocks/embed.twig
Normal file
|
@ -0,0 +1,12 @@
|
|||
<figure class="block-embed">
|
||||
<iframe
|
||||
class="block-embed__iframe"
|
||||
src="{{ embed }}"
|
||||
frameborder="0"
|
||||
></iframe>
|
||||
{% if caption %}
|
||||
<footer class="block-iframe__caption">
|
||||
{{ caption }}
|
||||
</footer>
|
||||
{% endif %}
|
||||
</figure>
|
7
src/backend/views/pages/blocks/header.twig
Normal file
7
src/backend/views/pages/blocks/header.twig
Normal file
|
@ -0,0 +1,7 @@
|
|||
<a name="{{ text | urlify }}" style="display: inline-block; position: absolute; margin-top: -20px;"></a>
|
||||
<h{{ level }} class="block-header block-header--{{ level }} block-header--anchor">
|
||||
<a href="#{{ text | urlify }}">
|
||||
{{ text }}
|
||||
</a>
|
||||
</h{{ level }}>
|
||||
|
31
src/backend/views/pages/blocks/image.twig
Normal file
31
src/backend/views/pages/blocks/image.twig
Normal file
|
@ -0,0 +1,31 @@
|
|||
{% set classes = ['block-image__content'] %}
|
||||
|
||||
{% if withBorder %}
|
||||
{% set classes = classes|merge(['block-image__content--bordered']) %}
|
||||
{% endif %}
|
||||
|
||||
{% if stretched %}
|
||||
{% set classes = classes|merge(['block-image__content--stretched']) %}
|
||||
{% endif %}
|
||||
|
||||
{% if withBackground %}
|
||||
{% set classes = classes|merge(['block-image__content--with-background']) %}
|
||||
{% endif %}
|
||||
|
||||
<figure class="block-image">
|
||||
<div class="{{ classes.join(' ') }}">
|
||||
{% if file.mime and file.mime == 'video/mp4' %}
|
||||
<video autoplay loop muted playsinline>
|
||||
<source src="{{ file.url }}" type="video/mp4">
|
||||
</video>
|
||||
{% else %}
|
||||
<img src="{{ file.url }}" alt="{{ caption ? caption | striptags : '' }}">
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% if caption %}
|
||||
<footer class="block-image__caption">
|
||||
{{ caption }}
|
||||
</footer>
|
||||
{% endif %}
|
||||
</figure>
|
17
src/backend/views/pages/blocks/linkTool.twig
Normal file
17
src/backend/views/pages/blocks/linkTool.twig
Normal file
|
@ -0,0 +1,17 @@
|
|||
<a class="block-link" href="{{ link }}" target="_blank" rel="nofollow">
|
||||
{% if meta.image.url %}
|
||||
<img class="block-link__image" src="{{ meta.image.url }}">
|
||||
{% endif %}
|
||||
|
||||
<div class="block-link__title">
|
||||
{{ meta.title }}
|
||||
</div>
|
||||
|
||||
<div class="block-link__description">
|
||||
{{ meta.description }}
|
||||
</div>
|
||||
|
||||
<span class="block-link__domain">
|
||||
{{ parseLink(link).hostname }}
|
||||
</span>
|
||||
</a>
|
13
src/backend/views/pages/blocks/list.twig
Normal file
13
src/backend/views/pages/blocks/list.twig
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% set tag = 'ul' %}
|
||||
|
||||
{% if style == 'ordered' %}
|
||||
{% set tag = 'ol' %}
|
||||
{% endif %}
|
||||
|
||||
<{{ tag }} class="block-list block-list--{{ style }}">
|
||||
{% for item in items %}
|
||||
<li>
|
||||
{{ item }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</{{ tag }}>
|
3
src/backend/views/pages/blocks/paragraph.twig
Normal file
3
src/backend/views/pages/blocks/paragraph.twig
Normal file
|
@ -0,0 +1,3 @@
|
|||
<p class="block-paragraph">
|
||||
{{ text }}
|
||||
</p>
|
1
src/backend/views/pages/blocks/raw.twig
Normal file
1
src/backend/views/pages/blocks/raw.twig
Normal file
|
@ -0,0 +1 @@
|
|||
{{ html }}
|
11
src/backend/views/pages/blocks/table.twig
Normal file
11
src/backend/views/pages/blocks/table.twig
Normal file
|
@ -0,0 +1,11 @@
|
|||
<table class="block-table">
|
||||
{% for row in content %}
|
||||
<tr>
|
||||
{% for cell in row %}
|
||||
<td>
|
||||
{{ cell }}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
13
src/backend/views/pages/blocks/warning.twig
Normal file
13
src/backend/views/pages/blocks/warning.twig
Normal file
|
@ -0,0 +1,13 @@
|
|||
<div class="block-warning">
|
||||
<div class="block-warning__icon">
|
||||
☝️
|
||||
</div>
|
||||
{% if title is not empty %}
|
||||
<div class="block-warning__title">
|
||||
{{ title }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="block-warning__message">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
67
src/backend/views/pages/form.twig
Normal file
67
src/backend/views/pages/form.twig
Normal file
|
@ -0,0 +1,67 @@
|
|||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<style>
|
||||
.docs-header__button {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
<section data-module="writing">
|
||||
<textarea name="module-settings" hidden>
|
||||
{
|
||||
"page": {{ page | json_encode | escape }}
|
||||
}
|
||||
</textarea>
|
||||
<header class="writing-header">
|
||||
<span class="writing-header__left">
|
||||
<span>
|
||||
New Page at the
|
||||
{% set currentPageId = 0 %}
|
||||
{% if page is not empty %}
|
||||
{% set currentPageId = page._id %}
|
||||
{% endif %}
|
||||
<select name="parent">
|
||||
<option value="0">Root</option>
|
||||
{% for _page in pagesAvailable %}
|
||||
{% if _page._id != currentPageId %}
|
||||
<option value="{{ _page._id }}" {{ page is not empty and page._parent == _page._id ? 'selected' : ''}}>
|
||||
{% if _page._parent != "0" %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
{{ _page.title }}
|
||||
</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
|
||||
{% if parentsChildrenOrdered is not empty %}
|
||||
<span>
|
||||
Put Above
|
||||
<select name="above">
|
||||
<option value="0">—</option>
|
||||
{% for _page in parentsChildrenOrdered %}
|
||||
<option value="{{ _page._id }}">{{ _page.title }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
{% if page is not empty %}
|
||||
<p><input type="text" class="uri-input" name="uri-input" placeholder="URI (Optional)" value="{{ page.uri }}"></p>
|
||||
{% endif %}
|
||||
</header>
|
||||
<div class="writing-editor">
|
||||
<div id="editorjs"></div>
|
||||
</div>
|
||||
<div class="writing-buttons">
|
||||
<span class="writing-header__save" name="js-submit-save">Save</span>
|
||||
|
||||
{% if page._id is not empty %}
|
||||
<span class="writing-buttons__remove" name="js-submit-remove">Remove</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
36
src/backend/views/pages/index.twig
Normal file
36
src/backend/views/pages/index.twig
Normal file
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<html style="height: 100%">
|
||||
<head>
|
||||
<title>{{ config.title }}</title>
|
||||
<link rel="stylesheet" href="/dist/main.css" />
|
||||
<link rel="preload" href="{{ config.landingFrameSrc }}" as="document">
|
||||
<link rel="icon" type="image/png" href="/favicon.png?v=2">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta property="og:title" content="{{ config.title }}" />
|
||||
<meta property="og:site_name" content="{{ config.title }}" />
|
||||
<meta name="description" property="og:description" content="{{ config.description }}">
|
||||
</head>
|
||||
<body class="landing-body">
|
||||
{% include "components/header.twig" %}
|
||||
<div class="landing-loader" id="frame-loader">
|
||||
{{ svg('loader') }}
|
||||
</div>
|
||||
<iframe class="landing-frame" src="{{ config.landingFrameSrc }}" seamless frameborder="0" onload="this.style.opacity = 1; setTimeout(document.getElementById('frame-loader').remove(), 500)"></iframe>
|
||||
|
||||
{% if config.yandexMetrikaId is not empty %}
|
||||
<script type="text/javascript" >
|
||||
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
||||
m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
|
||||
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
|
||||
|
||||
ym({{ config.yandexMetrikaId }}, "init", {
|
||||
clickmap:true,
|
||||
trackLinks:true,
|
||||
accurateTrackBounce:true
|
||||
});
|
||||
</script>
|
||||
<noscript><div><img src="https://mc.yandex.ru/watch/{{ config.yandexMetrikaId }}" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
|
48
src/backend/views/pages/page.twig
Normal file
48
src/backend/views/pages/page.twig
Normal file
|
@ -0,0 +1,48 @@
|
|||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<article class="page" data-module="page">
|
||||
<header class="page__header">
|
||||
<a href="/" class="page__header-nav">
|
||||
Documentation
|
||||
</a>
|
||||
{% if page._parent %}
|
||||
<a class="page__header-nav"
|
||||
{% if pageParent.uri %}
|
||||
href="/{{ pageParent.uri }}"
|
||||
{% else %}
|
||||
href="/page/{{ pageParent._id }}"
|
||||
{% endif %}>
|
||||
{{ pageParent.title }}
|
||||
</a>
|
||||
{% endif %}
|
||||
<time class="page__header-time">
|
||||
Last edit {{ (page.body.time / 1000) | date("M d Y") }}
|
||||
{% if isAuthorized == true %}
|
||||
<a href="/page/edit/{{ page._id }}" class="page__header-button">
|
||||
Edit
|
||||
</a>
|
||||
{% endif %}
|
||||
</time>
|
||||
</header>
|
||||
<h1 class="page__title">
|
||||
{{ page.title }}
|
||||
</h1>
|
||||
{% if (config.carbon and config.carbon.placement and config.carbon.serve) %}
|
||||
<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve={{ config.carbon.serve }}&placement={{ config.carbon.placement }}" id="_carbonads_js"></script>
|
||||
{% endif %}
|
||||
<section class="page__content">
|
||||
{% for block in page.body.blocks %}
|
||||
{# Skip first header, because it is already showed as a Title #}
|
||||
{% if not (loop.first and block.type == 'header') %}
|
||||
{% if block.type in ['paragraph', 'header', 'image', 'code', 'list', 'delimiter', 'table', 'warning', 'checklist', 'linkTool', 'raw', 'embed'] %}
|
||||
{% include './blocks/' ~ block.type ~ '.twig' with block.data %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</section>
|
||||
<footer>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue