commit 382b315e9660723571394cbc71746d9bb31820ed Author: bruno Date: Sun Jul 31 16:45:47 2022 -0400 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f748a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +main +data/website.db \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/code/database_utils.nim b/code/database_utils.nim new file mode 100644 index 0000000..cb55a59 --- /dev/null +++ b/code/database_utils.nim @@ -0,0 +1,98 @@ +# Copyright 2019 - Thomas T. Jarløv + +# import db_sqlite, os, parsecfg, strutils, logging +import db_sqlite, os, parsecfg, logging + +import ../code/password_utils + +proc generateDB*() = + echo "Generating database" + + # Load the connection details + let + dict = loadConfig("config/config.cfg") + db_user = dict.getSectionValue("Database","user") + db_pass = dict.getSectionValue("Database","pass") + db_name = dict.getSectionValue("Database","name") + db_host = dict.getSectionValue("Database","host") + db_folder = dict.getSectionValue("Database","folder") + dbexists = if fileExists(db_host): true else: false + + if dbexists: + echo " - Database already exists. Inserting tables if they do not exist." + + # Creating database folder if it doesn't exist + discard existsOrCreateDir(db_folder) + + # Open DB + echo " - Opening database" + var db = open(connection=db_host, user=db_user, password=db_pass, database=db_name) + + # Person table contains information about the + # registrered users + if not db.tryExec(sql(""" + create table if not exists person( + id integer primary key, + name varchar(60) not null, + password varchar(300) not null, + email varchar(254) not null, + creation timestamp not null default (STRFTIME('%s', 'now')), + modified timestamp not null default (STRFTIME('%s', 'now')), + salt varbin(128) not null, + status varchar(30) not null, + timezone VARCHAR(100), + secretUrl VARCHAR(250), + lastOnline timestamp not null default (STRFTIME('%s', 'now')) + );""")): + echo " - Database: person table already exists" + + # Session table contains information about the users + # cookie ID, IP and last visit + if not db.tryExec(sql(""" + create table if not exists session( + id integer primary key, + ip inet not null, + key varchar(300) not null, + userid integer not null, + lastModified timestamp not null default (STRFTIME('%s', 'now')), + foreign key (userid) references person(id) + );""")): + echo " - Database: session table already exists" + + +proc createAdminUser*(db: DbConn, args: seq[string]) = + ## Create new admin user + + var iName = "" + var iEmail = "" + var iPwd = "" + + # Loop through all the arguments and get the args + # containing the user information + for arg in args: + if arg.substr(0, 1) == "u:": + iName = arg.substr(2, arg.len()) + elif arg.substr(0, 1) == "p:": + iPwd = arg.substr(2, arg.len()) + elif arg.substr(0, 1) == "e:": + iEmail = arg.substr(2, arg.len()) + + # If the name, password or emails does not exists + # return error + if iName == "" or iPwd == "" or iEmail == "": + error("Missing either name, password or email to create the Admin user.") + + # Generate the password using a salt and hashing. + # Read more about hashing and salting here: + # - https://crackstation.net/hashing-security.htm + # - https://en.wikipedia.org/wiki/Salt_(cryptography) + let salt = makeSalt() + let password = makePassword(iPwd, salt) + + # Insert user into database + if insertID(db, sql"INSERT INTO person (name, email, password, salt, status) VALUES (?, ?, ?, ?, ?)", $iName, $iEmail, password, salt, "Admin") > 0: + echo "Admin user added" + else: + error("Something went wrong") + + info("Admin added.") \ No newline at end of file diff --git a/code/password_utils.nim b/code/password_utils.nim new file mode 100644 index 0000000..1ce6e5b --- /dev/null +++ b/code/password_utils.nim @@ -0,0 +1,34 @@ +# Copyright 2019 - Thomas T. Jarløv +# Credit Nimforum - https://github.com/nim-lang/nimforum + +# import md5, bcrypt, math, random, os +import md5, bcrypt, random +randomize() + +var urandom: File +let useUrandom = urandom.open("/dev/urandom") + +proc makeSalt*(): string = + ## Generate random salt. Uses cryptographically secure /dev/urandom + ## on platforms where it is available, and Nim's random module in other cases. + result = "" + if useUrandom: + var randomBytes: array[0..127, char] + discard urandom.readBuffer(addr(randomBytes), 128) + for ch in randomBytes: + if ord(ch) in {32..126}: + result.add(ch) + else: + for i in 0..127: + result.add(chr(rand(94) + 32)) # Generate numbers from 32 to 94 + 32 = 126 + +proc makeSessionKey*(): string = + ## Creates a random key to be used to authorize a session. + let random = makeSalt() + return bcrypt.hash(random, genSalt(8)) + + +proc makePassword*(password, salt: string, comparingTo = ""): string = + ## Creates an MD5 hash by combining password and salt + let bcryptSalt = if comparingTo != "": comparingTo else: genSalt(8) + result = hash(getMD5(salt & getMD5(password)), bcryptSalt) \ No newline at end of file diff --git a/config/config.cfg b/config/config.cfg new file mode 100644 index 0000000..760179e --- /dev/null +++ b/config/config.cfg @@ -0,0 +1,12 @@ +[Database] +folder = "data" +host = "data/website.db" +name = "website" +user = "user" +pass = "" + +[Server] +website = "https://myurl.org" +title = "Joplin The New Web" +url = "127.0.0.1" +port = "7000" \ No newline at end of file diff --git a/main.nim b/main.nim new file mode 100644 index 0000000..3ac8ff2 --- /dev/null +++ b/main.nim @@ -0,0 +1,227 @@ +# Copyright 2019 - Thomas T. Jarløv + +import db_sqlite # SQLite +import jester # Our webserver +import logging # Logging utils +import os # Used to get arguments +import parsecfg # Parse CFG (config) files +import strutils # Basic functions +import times # Time and date +import uri # We need to encode urls: encodeUrl() + +import code/database_utils # Utils used in the database +import code/password_utils # Our file with password utils + + +# First we'll load config files +let dict = loadConfig("config/config.cfg") + + +# Now we get the values and assign them. +# We do not need to change them later, therefore +# we'll use `let` +let db_user = dict.getSectionValue("Database", "user") +let db_pass = dict.getSectionValue("Database", "pass") +let db_name = dict.getSectionValue("Database", "name") +let db_host = dict.getSectionValue("Database", "host") + +let mainURL = dict.getSectionValue("Server", "url") +let mainPort = parseInt dict.getSectionValue("Server", "port") +let mainWebsite = dict.getSectionValue("Server", "website") + + +# Database var +var db: DbConn + + +# Jester setting server settings +settings: + port = Port(mainPort) + bindAddr = mainURL + + +# Setup user data +type + TData* = ref object of RootObj + loggedIn*: bool + userid, username*, userpass*, email*: string + req*: Request + + +proc init(c: var TData) = + ## Empty out user session data + c.userpass = "" + c.username = "" + c.userid = "" + c.loggedIn = false + + +func loggedIn(c: TData): bool = + ## Check if user is logged in by verifying that c.username exists + c.username.len > 0 + + +proc checkLoggedIn(c: var TData) = + ## Check if user is logged in + + # Get the users cookie named `sid`. If it does not exist, return + if not c.req.cookies.hasKey("sid"): return + + # Assign cookie to `let sid` + let sid = c.req.cookies["sid"] + + # Update the value lastModified for the user in the + # table session where the sid and IP match. If there's + # any results (above 0) assign values + if execAffectedRows(db, sql("UPDATE session SET lastModified = " & $toInt(epochTime()) & " " & "WHERE ip = ? AND key = ?"), c.req.ip, sid) > 0: + + # Get user data based on userID from session table + # Assign values to user details - `c` + c.userid = getValue(db, sql"SELECT userid FROM session WHERE ip = ? AND key = ?", c.req.ip, sid) + + # Get user data based on userID from person table + let row = getRow(db, sql"SELECT name, email, status FROM person WHERE id = ?", c.userid) + + # Assign user data + c.username = row[0] + c.email = toLowerAscii(row[1]) + + # Update our session table with info about activity + discard tryExec(db, sql"UPDATE person SET lastOnline = ? WHERE id = ?", toInt(epochTime()), c.userid) + + else: + # If the user is not found in the session table + c.loggedIn = false + + +proc login(c: var TData, email, pass: string): tuple[b: bool, s: string] = + ## User login + + # We have predefined query + const query = sql"SELECT id, name, password, email, salt, status FROM person WHERE email = ?" + + # If the email or pass passed in the proc's parameters is empty, fail + if email.len == 0 or pass.len == 0: + return (false, "Missing password or username") + + # We'll use fastRows for a quick query. + # Notice that the email is set to lower ascii + # to avoid problems if the user has any + # capitalized letters. + for row in fastRows(db, query, toLowerAscii(email)): + + # Now our password library is going to work. It'll + # check the password against the hashed password + # and salt. + if row[2] == makePassword(pass, row[4], row[2]): + # Assign the values + c.userid = row[0] + c.username = row[1] + c.userpass = row[2] + c.email = toLowerAscii(row[3]) + + # Generate session key and save it + let key = makeSessionKey() + exec(db, sql"INSERT INTO session (ip, key, userid) VALUES (?, ?, ?)", c.req.ip, key, row[0]) + + info("Login successful") + return (true, key) + + info("Login failed") + return (false, "Login failed") + + +proc logout(c: var TData) = + ## Logout + + c.username = "" + c.userpass = "" + const query = sql"DELETE FROM session WHERE ip = ? AND key = ?" + exec(db, query, c.req.ip, c.req.cookies["sid"]) + + +# Do the check inside our routes +template createTFD() = + ## Check if logged in and assign data to user + + # Assign the c to TDATA + var c {.inject.}: TData + # New instance of c + new(c) + # Set standard values + init(c) + # Get users request + c.req = request + # Check for cookies (we need the cookie named sid) + if cookies(request).len > 0: + # Check if user is logged in + checkLoggedIn(c) + # Use the func() + c.loggedIn = loggedIn(c) + + +# isMainModule +when isMainModule: + echo "Nim Web is now running: " & $now() + + # Generate DB if newdb is in the arguments + # or if the database does not exists + if "newdb" in commandLineParams() or not fileExists(db_host): + generateDB() + quit() + + # Connect to DB + try: + # We are using the values which we assigned earlier + db = open(connection=db_host, user=db_user, password=db_pass, database=db_name) + info("Connection to DB is established.") + except: + fatal("Connection to DB could not be established.") + sleep(5_000) + quit() + + # Add an admin user if newuser is in the args + if "newuser" in commandLineParams(): + createAdminUser(db, commandLineParams()) + quit() + + +# Include template files +#include "tmpl/main.tmpl" +include "tmpl/user.tmpl" +include "tmpl/website.tmpl" + + +# Setup routes (URL's) +routes: + get "/": + createTFD() + resp genMain(c) + + get "/secret": + createTFD() + if c.loggedIn: + resp genSecret(c) + + get "/login": + createTFD() + resp genLogin(c, @"msg") + + post "/dologin": + createTFD() + + let (loginB, loginS) = login(c, replace(toLowerAscii(@"email"), " ", ""), replace(@"password", " ", "")) + if loginB: + when defined(dev): + jester.setCookie("sid", loginS, daysForward(7)) + else: + jester.setCookie("sid", loginS, daysForward(7), samesite = Lax, secure = true, httpOnly = true) + + redirect("/secret") + else: + redirect("/login?msg=" & encodeUrl(loginS)) + + get "/logout": + createTFD() + logout(c) + redirect("/") diff --git a/public/index.html b/public/index.html new file mode 100755 index 0000000..aa2dcd9 --- /dev/null +++ b/public/index.html @@ -0,0 +1,316 @@ + + + + + + + Document + + + + +
+ +
+
+ +
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex rem ipsum porro delectus ipsa placeat modi aut non blanditiis, consequatur earum provident atque quaerat. Sint veniam facere minima nostrum exercitationem sit iste placeat iure non officia adipisci porro similique dolorum vero temporibus corrupti, incidunt, fugit recusandae. Eos voluptas ipsam reprehenderit fugit ad obcaecati ipsum ratione officiis ipsa. Voluptas nam modi, commodi rerum repellendus quae amet, illum expedita esse quis officiis impedit cumque deserunt ipsum asperiores corrupti nulla repudiandae atque mollitia soluta sit ea? A ratione voluptatem sapiente excepturi possimus, ad, atque repellat iusto, architecto iste commodi ut? Cupiditate aliquid quae ad quaerat maxime, ut recusandae perferendis tempore itaque reiciendis fugiat, odio ea vero non illo voluptatibus laborum! Dolore molestias ab hic? Dolorem, eveniet dolores non at voluptatum laudantium recusandae sapiente reprehenderit soluta maxime provident vero vel maiores aperiam ullam eum necessitatibus quibusdam? Corrupti necessitatibus corporis et, dolore reiciendis dolorum dolor earum recusandae incidunt optio nostrum accusantium? Dignissimos et modi adipisci illo labore rerum! Sed libero doloremque repellendus quaerat iste veniam, error adipisci sint porro exercitationem obcaecati eum, perspiciatis illo aliquam enim debitis assumenda reiciendis architecto repudiandae sequi velit! Quos ad enim sapiente autem incidunt debitis. Quaerat nam praesentium, beatae iste vel consectetur doloremque laboriosam asperiores hic nobis assumenda laudantium amet illum, repellat facere enim sequi reprehenderit at et! Expedita blanditiis nulla vitae corporis pariatur eius tenetur fuga, natus cumque autem similique soluta voluptate excepturi esse dolores deleniti ducimus illo exercitationem! Suscipit perferendis debitis ad reiciendis nesciunt, maxime magni labore fugiat soluta perspiciatis commodi, veniam illo nihil magnam exercitationem at, itaque consectetur unde modi accusantium similique. Cum fugit maiores enim, tempora culpa aliquam accusamus debitis consequuntur libero, sed voluptate distinctio voluptatum impedit praesentium incidunt? Placeat laboriosam sunt tenetur quod beatae eum unde error eos repellat! Eaque eius rerum porro dolore, cupiditate quisquam vel. Ipsam, quos accusantium incidunt fugit explicabo minus aliquid sed ullam sapiente, quo facilis dolorem excepturi sunt nam corporis, amet perferendis repellat eum reprehenderit! Autem cum alias veritatis consequuntur harum aspernatur culpa, aliquid voluptas natus in repellendus at rem tenetur praesentium dolores, et minima. Doloribus reprehenderit fugiat dolores tempora repudiandae id officiis aperiam, a quaerat alias corrupti praesentium totam in obcaecati aspernatur aliquid odio velit quo necessitatibus, maiores veritatis quidem nisi recusandae! Accusantium non dolorem natus eaque dicta sed impedit esse sit obcaecati laudantium dolore magni, excepturi cum amet perspiciatis! Dolor cumque excepturi voluptas tenetur! Accusamus, officia amet unde eos sit est sapiente neque nobis quos rerum accusantium, debitis quisquam obcaecati? Sit provident sint dolore ipsum voluptas modi aut eligendi quibusdam natus est ex quasi culpa, iste iure enim quisquam vero dicta aspernatur. Placeat harum dolores sapiente porro ut. Iusto, dolores earum sed perferendis, illum aliquam est necessitatibus, nesciunt laborum sunt exercitationem nostrum blanditiis neque suscipit ipsam? Quas ea dicta nihil, natus necessitatibus blanditiis praesentium iure consequuntur voluptatem perferendis, neque iusto commodi officia minima ipsa eligendi tempora laborum tenetur, quaerat atque fugit accusantium minus impedit. Quaerat impedit officiis odit aliquam animi, magni delectus eius sunt fuga. Ipsa doloremque doloribus eveniet iusto velit quod repellat tenetur consequatur aliquid sapiente beatae voluptates quaerat fugit ratione odio sint, adipisci earum obcaecati quisquam. Ratione similique tempora cumque eos ducimus sunt veritatis eius distinctio maxime rerum aut voluptatibus temporibus, eum cum ex neque sint non facere, eligendi officia. Consequuntur reprehenderit, ipsa ex quod necessitatibus ratione nemo illo doloremque saepe perferendis cupiditate praesentium consectetur provident totam inventore exercitationem, dolorem accusantium debitis voluptates amet sunt odit. At reprehenderit illum, sed fuga a cupiditate amet dolorem, non eos nobis eaque recusandae facere saepe esse. Magnam incidunt, eveniet ipsa et, ea assumenda in repudiandae omnis itaque voluptatum quibusdam atque, reprehenderit totam quis saepe quod obcaecati cumque ratione perferendis eaque corporis doloremque! Sed maiores iure aliquam unde similique amet eveniet sapiente cupiditate, deserunt quia repudiandae, voluptates numquam ipsa cumque perspiciatis facilis tempora! Esse consectetur modi libero incidunt tenetur, nisi voluptas quasi ratione eligendi nostrum itaque, excepturi veritatis delectus. Vero dolorum optio accusantium, quaerat aut iure beatae explicabo quas amet dolores cupiditate eveniet magni facere, vel consectetur repudiandae nesciunt voluptates consequatur ad facilis distinctio neque blanditiis illum unde! Est sit consectetur delectus quos labore! Eius expedita hic repellendus. Reprehenderit suscipit fugit cum id vero, consequatur expedita sint quae sit, pariatur aspernatur harum cupiditate debitis tempora magnam quod praesentium autem asperiores. Modi, architecto. Non nostrum sint porro minus itaque neque quas. Minus nihil enim eveniet. Obcaecati error provident id, temporibus molestiae consequuntur cumque odit, consectetur ducimus dolor illum vel illo numquam nam minima quisquam minus debitis eius fuga. Illo culpa id assumenda ipsam? Nemo accusantium quibusdam fugiat adipisci expedita nostrum iste eum quas, animi sequi quaerat sint aspernatur tenetur maiores saepe quidem? Ab reprehenderit cum quae quidem ullam repellendus vel aliquid ipsam veritatis quaerat. Repellendus, recusandae adipisci aperiam est dolorum quos, obcaecati repudiandae optio esse nemo eveniet id accusantium maxime architecto. Praesentium corporis architecto, pariatur fugit quam ducimus exercitationem a sit et cupiditate facere est eum ipsa ut illo. Corrupti eum id quo quisquam. Sequi eaque veniam quam fuga error explicabo, distinctio beatae quasi doloribus consequuntur maxime. Libero inventore labore in obcaecati doloribus voluptas facilis cumque atque voluptatem magnam ex tempore, necessitatibus corporis, velit ut modi eligendi, qui explicabo eaque sit cum illum. Ipsum delectus numquam quisquam adipisci velit ad laboriosam cum ab. Saepe at perspiciatis ratione nostrum ipsam voluptas incidunt inventore libero dolores blanditiis nemo, eos esse earum neque recusandae? Ab quidem temporibus similique commodi asperiores tempore reprehenderit accusamus. Reiciendis suscipit ullam alias sint impedit labore laudantium vel. Ratione, sequi quam? Adipisci quidem doloribus dignissimos omnis ad optio ex, numquam aperiam aut rerum, sequi dolorum natus tempora quis? Assumenda corrupti reprehenderit rerum aut eos numquam optio unde necessitatibus omnis perferendis iusto, perspiciatis quis eaque possimus asperiores veniam placeat temporibus eveniet ratione minima pariatur sequi! Neque doloremque sunt dolor repudiandae, similique asperiores. Totam, ipsum cupiditate! Facere quidem aliquid illum magni deleniti at et necessitatibus tempora. Suscipit iusto, odit alias distinctio aspernatur voluptates, eveniet, ipsam ducimus nam harum ad iste? Molestias odit distinctio aspernatur odio obcaecati perferendis maxime, excepturi et explicabo sed?
+
+ + \ No newline at end of file diff --git a/public/logo.html b/public/logo.html new file mode 100755 index 0000000..025e724 --- /dev/null +++ b/public/logo.html @@ -0,0 +1,72 @@ + + + + + + + + Document + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/logo.png b/public/logo.png new file mode 100755 index 0000000..1495367 Binary files /dev/null and b/public/logo.png differ diff --git a/public/script.js b/public/script.js new file mode 100755 index 0000000..ec95081 --- /dev/null +++ b/public/script.js @@ -0,0 +1,6 @@ +const menuIconButton = document.querySelector("[data-menu-icon-btn]") +const sidebar = document.querySelector("[data-sidebar]") + +menuIconButton.addEventListener("click", () => { + sidebar.classList.toggle("open") +}) diff --git a/public/styles.css b/public/styles.css new file mode 100755 index 0000000..532c197 --- /dev/null +++ b/public/styles.css @@ -0,0 +1,253 @@ +body { + margin: 0; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +:root { + --accent-color: #0053b8; + --lightest-gray: rgb(244, 244, 244); + --light-gray: rgb(144, 144, 144); + --medium-gray: rgb(96, 96, 96); + --dark-gray: rgb(13, 13, 13); + --header-height: 40px; + --animation-duration: 200ms; + --animation-timing-curve: ease-in-out; + --blue-joplin-color: #0053b8; + --light-blue-joplin-color: rgb(237, 241, 243); +} + +.header { + display: flex; + align-items: center; + position: sticky; + top: 0; + background-color: white; + box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.4); + padding: 0 0.5rem; + height: var(--header-height); +} + +.login { + font-size: large; + fill: var(--blue-joplin-color); + max-width: fit-content; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + +} + +.menu-icon-btn { + background: none; + border: none; + padding: 0; +} + +.menu-icon { + width: 25px; + height: 25px; + fill: var(--medium-gray); + cursor: pointer; +} + +.menu-icon:hover { + fill: var(--dark-gray); +} + +.sidebar { + flex-shrink: 0; + overflow: hidden; + width: 75px; + border-right: 1px solid var(--light-gray); + display: flex; + flex-direction: column; + height: calc(100vh - var(--header-height)); + padding-top: 1rem; + align-items: center; + justify-content: stretch; + transition: width var(--animation-duration) var(--animation-timing-curve); + position: sticky; + left: 0; + top: var(--header-height); +} + +.sidebar .hidden-sidebar { + opacity: 0; + width: 0; + transition: opacity var(--animation-duration) var(--animation-timing-curve); +} + +.sidebar.open .hidden-sidebar { + width: 100%; + height: auto; + opacity: 1; +} + +.sidebar .top-sidebar { + display: flex; + flex-direction: column; + align-items: center; +} + +.sidebar .channel-logo { + display: block; + width: 30px; + height: 30px; + transition: var(--animation-duration) var(--animation-timing-curve); +} + +.sidebar.open .channel-logo { + width: 90px; + height: 90px; +} + +.sidebar .channel-logo > img { + width: 100%; + height: 100%; +} + +.middle-sidebar { + overflow-y: auto; + overflow-x: hidden; + flex-grow: 1; + margin: 1rem 0; +} + +.middle-sidebar, +.bottom-sidebar { + width: 100%; +} + +.container { + display: flex; +} + +.vertical-center { + margin: 0; + position: absolute; + top: 50%; + -ms-transform: translateY(-50%); + transform: translateY(-50%); + /* border: 5px solid #FFFF00; */ + text-align: center; +} + +.content { + margin: 1rem; +} + +.sidebar-list { + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + align-items: center; + list-style: none; +} + +.sidebar.open .sidebar-link { + justify-content: flex-start; +} + +.sidebar-icon { + width: 25px; + height: 25px; + flex-shrink: 0; + fill: var(--blue-joplin-color); +} + +.sidebar-list .hidden-sidebar { + margin-left: 1.5rem; + white-space: nowrap; +} + +.sidebar-link { + display: flex; + width: 100%; + padding: 0.7rem 0; + color: var(--light-gray); + text-decoration: none; + align-items: center; + padding-left: 25px; +} + +.sidebar-list-item { + position: relative; + width: 100%; + fill: var(--light-gray); +} + +.sidebar-list-item.active { + fill: var(--accent-color); + background-color: var(--light-blue-joplin-color); +} + +.sidebar-list-item:hover { + background-color: var(--light-blue-joplin-color); +} + +.sidebar-list-item.active::before { + content: ""; + background-color: var(--accent-color); + height: 100%; + left: 0; + width: 3px; + position: absolute; +} + +.sidebar.open { + width: 200px; +} + +.your-channel { + color: var(--dark-gray); + font-size: 0.75rem; + font-weight: bold; + margin-bottom: 0.15rem; + margin-top: 0.5rem; +} + +.channel-name { + color: var(--medium-gray); + font-size: 0.75rem; +} + +.sidebar .top-sidebar { + height: 30px; + transition: height var(--animation-duration) var(--animation-timing-curve); +} + +.sidebar.open .top-sidebar { + height: 125px; +} + +.sidebar .top-sidebar .hidden-sidebar { + text-align: center; + width: 100%; +} + +/* .svg-icon { + width: 2em; + height: 2em; +} + +.svg-icon:hover { + fill: var(--animation-duration); +} + +.svg-icon path, +.svg-icon polygon, +.svg-icon rect { + fill: #4691f6; +} + +.svg-icon circle { + stroke: #4691f6; + stroke-width: 1; +} */ diff --git a/tmpl/user.tmpl b/tmpl/user.tmpl new file mode 100644 index 0000000..03d2418 --- /dev/null +++ b/tmpl/user.tmpl @@ -0,0 +1,43 @@ +#? stdtmpl | standard +# +#proc genLogin(c: var TData, errorMsg = ""): string = +# result = "" +# if not c.loggedIn: + +
+ +
+ + #else: +
+ + +
+# end if +#end proc \ No newline at end of file diff --git a/tmpl/website.tmpl b/tmpl/website.tmpl new file mode 100644 index 0000000..d165362 --- /dev/null +++ b/tmpl/website.tmpl @@ -0,0 +1,344 @@ +#? stdtmpl | standard +# +#proc genMain(c: var TData): string = +# result = "" + + + + + + + +#end proc +# +#proc genSecret(c: var TData): string = +# result = "" + + + + + + + Document + + + + +
+ +
+
+ +
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex rem ipsum porro delectus ipsa placeat modi aut non blanditiis, consequatur earum provident atque quaerat. Sint veniam facere minima nostrum exercitationem sit iste placeat iure non officia adipisci porro similique dolorum vero temporibus corrupti, incidunt, fugit recusandae. Eos voluptas ipsam reprehenderit fugit ad obcaecati ipsum ratione officiis ipsa. Voluptas nam modi, commodi rerum repellendus quae amet, illum expedita esse quis officiis impedit cumque deserunt ipsum asperiores corrupti nulla repudiandae atque mollitia soluta sit ea? A ratione voluptatem sapiente excepturi possimus, ad, atque repellat iusto, architecto iste commodi ut? Cupiditate aliquid quae ad quaerat maxime, ut recusandae perferendis tempore itaque reiciendis fugiat, odio ea vero non illo voluptatibus laborum! Dolore molestias ab hic? Dolorem, eveniet dolores non at voluptatum laudantium recusandae sapiente reprehenderit soluta maxime provident vero vel maiores aperiam ullam eum necessitatibus quibusdam? Corrupti necessitatibus corporis et, dolore reiciendis dolorum dolor earum recusandae incidunt optio nostrum accusantium? Dignissimos et modi adipisci illo labore rerum! Sed libero doloremque repellendus quaerat iste veniam, error adipisci sint porro exercitationem obcaecati eum, perspiciatis illo aliquam enim debitis assumenda reiciendis architecto repudiandae sequi velit! Quos ad enim sapiente autem incidunt debitis. Quaerat nam praesentium, beatae iste vel consectetur doloremque laboriosam asperiores hic nobis assumenda laudantium amet illum, repellat facere enim sequi reprehenderit at et! Expedita blanditiis nulla vitae corporis pariatur eius tenetur fuga, natus cumque autem similique soluta voluptate excepturi esse dolores deleniti ducimus illo exercitationem! Suscipit perferendis debitis ad reiciendis nesciunt, maxime magni labore fugiat soluta perspiciatis commodi, veniam illo nihil magnam exercitationem at, itaque consectetur unde modi accusantium similique. Cum fugit maiores enim, tempora culpa aliquam accusamus debitis consequuntur libero, sed voluptate distinctio voluptatum impedit praesentium incidunt? Placeat laboriosam sunt tenetur quod beatae eum unde error eos repellat! Eaque eius rerum porro dolore, cupiditate quisquam vel. Ipsam, quos accusantium incidunt fugit explicabo minus aliquid sed ullam sapiente, quo facilis dolorem excepturi sunt nam corporis, amet perferendis repellat eum reprehenderit! Autem cum alias veritatis consequuntur harum aspernatur culpa, aliquid voluptas natus in repellendus at rem tenetur praesentium dolores, et minima. Doloribus reprehenderit fugiat dolores tempora repudiandae id officiis aperiam, a quaerat alias corrupti praesentium totam in obcaecati aspernatur aliquid odio velit quo necessitatibus, maiores veritatis quidem nisi recusandae! Accusantium non dolorem natus eaque dicta sed impedit esse sit obcaecati laudantium dolore magni, excepturi cum amet perspiciatis! Dolor cumque excepturi voluptas tenetur! Accusamus, officia amet unde eos sit est sapiente neque nobis quos rerum accusantium, debitis quisquam obcaecati? Sit provident sint dolore ipsum voluptas modi aut eligendi quibusdam natus est ex quasi culpa, iste iure enim quisquam vero dicta aspernatur. Placeat harum dolores sapiente porro ut. Iusto, dolores earum sed perferendis, illum aliquam est necessitatibus, nesciunt laborum sunt exercitationem nostrum blanditiis neque suscipit ipsam? Quas ea dicta nihil, natus necessitatibus blanditiis praesentium iure consequuntur voluptatem perferendis, neque iusto commodi officia minima ipsa eligendi tempora laborum tenetur, quaerat atque fugit accusantium minus impedit. Quaerat impedit officiis odit aliquam animi, magni delectus eius sunt fuga. Ipsa doloremque doloribus eveniet iusto velit quod repellat tenetur consequatur aliquid sapiente beatae voluptates quaerat fugit ratione odio sint, adipisci earum obcaecati quisquam. Ratione similique tempora cumque eos ducimus sunt veritatis eius distinctio maxime rerum aut voluptatibus temporibus, eum cum ex neque sint non facere, eligendi officia. Consequuntur reprehenderit, ipsa ex quod necessitatibus ratione nemo illo doloremque saepe perferendis cupiditate praesentium consectetur provident totam inventore exercitationem, dolorem accusantium debitis voluptates amet sunt odit. At reprehenderit illum, sed fuga a cupiditate amet dolorem, non eos nobis eaque recusandae facere saepe esse. Magnam incidunt, eveniet ipsa et, ea assumenda in repudiandae omnis itaque voluptatum quibusdam atque, reprehenderit totam quis saepe quod obcaecati cumque ratione perferendis eaque corporis doloremque! Sed maiores iure aliquam unde similique amet eveniet sapiente cupiditate, deserunt quia repudiandae, voluptates numquam ipsa cumque perspiciatis facilis tempora! Esse consectetur modi libero incidunt tenetur, nisi voluptas quasi ratione eligendi nostrum itaque, excepturi veritatis delectus. Vero dolorum optio accusantium, quaerat aut iure beatae explicabo quas amet dolores cupiditate eveniet magni facere, vel consectetur repudiandae nesciunt voluptates consequatur ad facilis distinctio neque blanditiis illum unde! Est sit consectetur delectus quos labore! Eius expedita hic repellendus. Reprehenderit suscipit fugit cum id vero, consequatur expedita sint quae sit, pariatur aspernatur harum cupiditate debitis tempora magnam quod praesentium autem asperiores. Modi, architecto. Non nostrum sint porro minus itaque neque quas. Minus nihil enim eveniet. Obcaecati error provident id, temporibus molestiae consequuntur cumque odit, consectetur ducimus dolor illum vel illo numquam nam minima quisquam minus debitis eius fuga. Illo culpa id assumenda ipsam? Nemo accusantium quibusdam fugiat adipisci expedita nostrum iste eum quas, animi sequi quaerat sint aspernatur tenetur maiores saepe quidem? Ab reprehenderit cum quae quidem ullam repellendus vel aliquid ipsam veritatis quaerat. Repellendus, recusandae adipisci aperiam est dolorum quos, obcaecati repudiandae optio esse nemo eveniet id accusantium maxime architecto. Praesentium corporis architecto, pariatur fugit quam ducimus exercitationem a sit et cupiditate facere est eum ipsa ut illo. Corrupti eum id quo quisquam. Sequi eaque veniam quam fuga error explicabo, distinctio beatae quasi doloribus consequuntur maxime. Libero inventore labore in obcaecati doloribus voluptas facilis cumque atque voluptatem magnam ex tempore, necessitatibus corporis, velit ut modi eligendi, qui explicabo eaque sit cum illum. Ipsum delectus numquam quisquam adipisci velit ad laboriosam cum ab. Saepe at perspiciatis ratione nostrum ipsam voluptas incidunt inventore libero dolores blanditiis nemo, eos esse earum neque recusandae? Ab quidem temporibus similique commodi asperiores tempore reprehenderit accusamus. Reiciendis suscipit ullam alias sint impedit labore laudantium vel. Ratione, sequi quam? Adipisci quidem doloribus dignissimos omnis ad optio ex, numquam aperiam aut rerum, sequi dolorum natus tempora quis? Assumenda corrupti reprehenderit rerum aut eos numquam optio unde necessitatibus omnis perferendis iusto, perspiciatis quis eaque possimus asperiores veniam placeat temporibus eveniet ratione minima pariatur sequi! Neque doloremque sunt dolor repudiandae, similique asperiores. Totam, ipsum cupiditate! Facere quidem aliquid illum magni deleniti at et necessitatibus tempora. Suscipit iusto, odit alias distinctio aspernatur voluptates, eveniet, ipsam ducimus nam harum ad iste? Molestias odit distinctio aspernatur odio obcaecati perferendis maxime, excepturi et explicabo sed?
+
+ + +#end proc \ No newline at end of file