Compare commits

..

1 Commits

Author SHA1 Message Date
snyk-bot
be4fd6a762
feat: upgrade lru-cache from 9.1.2 to 10.0.0
Snyk has created this PR to upgrade lru-cache from 9.1.2 to 10.0.0.

See this package in npm:
https://www.npmjs.com/package/lru-cache

See this project in Snyk:
https://app.snyk.io/org/mystikfluu/project/c5b23892-809c-4121-9c2f-67962c7a4953?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-07-08 14:55:36 +00:00
78 changed files with 7848 additions and 10644 deletions

View File

@ -1,10 +0,0 @@
node_modules
npm-debug.log
.git
.gitignore
.env
*.md
logs
*.log
cookiesecret.txt
mysql_password.txt

View File

@ -4,27 +4,28 @@
name: Node.js CI name: Node.js CI
on: on:
push: push:
branches: ['master'] branches: [ "master" ]
pull_request: pull_request:
branches: ['master'] branches: [ "master" ]
jobs: jobs:
build: build:
runs-on: ubuntu-latest
strategy: runs-on: ubuntu-latest
matrix:
node-version: [12.x, 14.x, 16.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps: strategy:
- uses: actions/checkout@v3 matrix:
- name: Use Node.js ${{ matrix.node-version }} node-version: [12.x, 14.x, 16.x]
uses: actions/setup-node@v3 # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
with:
node-version: ${{ matrix.node-version }} steps:
cache: 'npm' - uses: actions/checkout@v3
- run: npm ci - name: Use Node.js ${{ matrix.node-version }}
- run: npm run build --if-present uses: actions/setup-node@v3
- run: npm test with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run build --if-present
- run: npm test

1
.husky/.gitignore vendored
View File

@ -1 +0,0 @@
_

View File

@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged

View File

@ -1,2 +0,0 @@
views/
pnpm-lock.yaml

View File

@ -1,4 +0,0 @@
trailingComma = "es5"
tabWidth = 4
semi = false
singleQuote = true

View File

@ -1,18 +0,0 @@
FROM node:slim
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy application code
COPY . .
# Expose ports
EXPOSE 80
# Start the application
CMD ["node", "server.js"]

View File

@ -1,4 +1,3 @@
# IPost # IPost
IPost, formerly known as "authwebsite" is a chatting platform that mainly has one thing in mind: privacy.
IPost, formerly known as "authwebsite" is a chatting platform that also server as a gateway for me to have authentication for my other projects.
You can visit IPost under https://ipost.rocks You can visit IPost under https://ipost.rocks

View File

@ -1,37 +1,37 @@
body { body {
text-align: center; text-align: center;
background: black; background: black;
} }
div { div {
align-items: center; align-items: center;
text-align: center; text-align: center;
border-radius: 20px; border-radius: 20px;
border: 5px solid black; border: 5px solid black;
display: inline-block; display: inline-block;
padding: 5%; padding: 5%;
padding-top: 1%; padding-top: 1%;
background-color: darkgray; background-color: darkgray;
} }
button { button {
border-radius: 5px; border-radius: 5px;
font-size: 19px; font-size: 19px;
background-color: purple; background-color: purple;
color: white; color: white;
} }
input { input {
border-radius: 8px; border-radius: 8px;
font-size: 20px; font-size: 20px;
text-align: center; text-align: center;
background-color: darkgray; background-color: darkgray;
color: black; color: black;
} }
label { label {
display: inline-block; display: inline-block;
width: 150px; width: 150px;
text-align: right; text-align: right;
font-size: 18px; font-size: 18px;
} }

View File

@ -1,132 +1,131 @@
:root { :root {
--green: #c2f9bb; /* links etc */ --green: #C2F9BB; /* links etc */
--fg-color: #303034; /* post background */ --fg-color: #303034; /* post background */
--bg-color: #1b1b1e; /* page background etc */ --bg-color: #1B1B1E; /* page background etc */
--text-color: #eceaf1; /* text */ --text-color: #ECEAF1; /* text */
--blue-ish: #587291; /* buttons etc */ --blue-ish: #587291; /* buttons etc */
} }
* { * {
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
} }
html { html {
scroll-behavior: smooth; scroll-behavior: smooth;
} }
body { body {
margin: 8px; margin: 8px;
} }
.noselect { .noselect {
user-select: none; user-select: none;
} }
#modal-shade, #modal-shade,
#modal { #modal {
display: none; display: none;
text-align: center; text-align: center;
} }
#modal-shade { #modal-shade {
position: fixed; position: fixed;
z-index: 100; z-index: 100;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
#modal { #modal {
z-index: 101; z-index: 101;
position: fixed; position: fixed;
width: 50%; width: 50%;
height: 50%; height:50%;
left: 25%; left:25%;
right: 25%; right:25%;
top: 25%; top:25%;
} }
#modal-shade { #modal-shade {
background: silver; background: silver;
opacity: 0.5; opacity: 0.5;
filter: alpha(opacity=50); filter: alpha(opacity=50);
} }
#modal { #modal {
background: rgba(0, 0, 0, 0.5); background: rgba(0,0,0,.5);
} }
ul { ul {
list-style-type: none; list-style-type: none;
margin: 0; margin: 0;
padding: 0; padding: 0;
overflow: hidden; overflow: hidden;
background-color: var(--fg-color); background-color: var(--fg-color);
} }
li { li {
float: left; float: left;
} }
li a { li a {
display: block; display: block;
color: white; color: white;
text-align: center; text-align: center;
padding: 14px 16px; padding: 14px 16px;
text-decoration: none; text-decoration: none;
} }
.totib { .totib {
margin-top: 6.5%; margin-top: 6.5%;
} }
li a:hover { li a:hover {
background-color: #111; background-color: #111;
} }
a { a {
text-decoration: none; text-decoration: none;
} }
a:link:not(.no-link-style), a:link:not(.no-link-style), a:visited:not(.no-link-style) {
a:visited:not(.no-link-style) { color: var(--green);
color: var(--green);
} }
.no-link-style:visited, .no-link-style:visited, .no-link-style:link {
.no-link-style:link { color: var(--text-color);
color: var(--text-color);
} }
a:hover { a:hover {
color: red; color: red;
} }
footer { footer {
color: white; color: white;
margin-top: 100px; margin-top: 100px;
background-color: var(--fg-color); background-color: var(--fg-color);
} }
.right { .right {
float: right; float: right;
} }
.less_padding { .less_padding {
padding: 6px 14px; padding: 6px 14px;
} }
.pd-great { .pd-great {
padding: 10px 30px; padding: 10px 30px;
} }
.bg-light { .bg-light {
background-color: var(--fg-color); background-color: var(--fg-color);
} }
.bg-dark { .bg-dark {
background-color: var(--bg-color); background-color: var(--bg-color);
} }
.no-bg-img { .no-bg-img {
background: none; background: none;
} }

View File

@ -1,99 +1,98 @@
* { * {
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
} }
body { body {
background-color: var(--bg-color); background-color: var(--bg-color);
} }
header { header {
color: white; color: white;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 15vh; height: 15vh;
box-shadow: 5px 5px 10px rgb(0, 0, 0, 0.3); box-shadow: 5px 5px 10px rgb(0,0,0,0.3);
} }
h1 { h1 {
letter-spacing: 1.5vw; letter-spacing: 1.5vw;
text-transform: uppercase; text-transform: uppercase;
text-align: center; text-align: center;
} }
main { main {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 75vh; height: 75vh;
width: 100%; width: 100%;
background: url(https://ipost.rocks/images/Mountains.webp) no-repeat center background: url(https://ipost.rocks/images/Mountains.webp) no-repeat center center;
center; background-size: cover;
background-size: cover;
} }
.form_class { .form_class {
width: 500px; width: 500px;
padding: 40px; padding: 40px;
border-radius: 8px; border-radius: 8px;
background-color: var(--bg-color); background-color: var(--bg-color);
color: var(--text-color); color: var(--text-color);
box-shadow: 5px 5px 10px rgb(0, 0, 0, 0.3); box-shadow: 5px 5px 10px rgb(0,0,0,.3);
} }
.form_div { .form_div {
text-transform: uppercase; text-transform: uppercase;
} }
.form_div > label { .form_div > label {
letter-spacing: 3px; letter-spacing: 3px;
font-size: 1rem; font-size: 1rem;
} }
.info_div { .info_div {
text-align: center; text-align: center;
margin-top: 20px; margin-top: 20px;
} }
.info_div { .info_div {
letter-spacing: 1px; letter-spacing: 1px;
} }
.field_class { .field_class {
width: 100%; width: 100%;
border-radius: 6px; border-radius: 6px;
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
padding: 5px 0px; padding: 5px 0px;
text-indent: 6px; text-indent: 6px;
margin-top: 10px; margin-top: 10px;
margin-bottom: 20px; margin-bottom: 20px;
font-size: 0.9rem; font-size: 0.9rem;
letter-spacing: 2px; letter-spacing: 2px;
} }
.submit_class { .submit_class {
border-style: none; border-style: none;
border-radius: 5px; border-radius: 5px;
background-color: #ffe6d4; background-color: #FFE6D4;
padding: 8px 20px; padding: 8px 20px;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.8px; letter-spacing: .8px;
display: block; display: block;
margin: auto; margin: auto;
margin-top: 10px; margin-top: 10px;
box-shadow: 2px 2px 5px rgb(0, 0, 0, 0.2); box-shadow: 2px 2px 5px rgb(0,0,0,0.2);
cursor: pointer; cursor: pointer;
} }
footer { footer {
height: 10vh; height: 10vh;
background-color: black; background-color: black;
color: white; color: white;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: -5px -5px 10px rgb(0, 0, 0, 0.3); box-shadow: -5px -5px 10px rgb(0,0,0,0.3);
} }
footer > p { footer > p {
text-align: center; text-align: center;
letter-spacing: 3px; letter-spacing: 3px;
} }
footer > p > a { footer > p > a {
text-decoration: none; text-decoration: none;
color: white; color: white;
font-weight: bold; font-weight: bold;
} }
a { a {
color: red; color: red;
text-decoration: none; text-decoration: none;
} }

View File

@ -1,47 +1,46 @@
* { * {
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
} }
body { body {
background-color: lightgreen; background-color: lightgreen;
} }
header { header {
background-color: black; background-color: black;
color: white; color: white;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 15vh; height: 15vh;
box-shadow: 5px 5px 10px rgb(0, 0, 0, 0.3); box-shadow: 5px 5px 10px rgb(0,0,0,0.3);
} }
h1 { h1 {
letter-spacing: 1.5vw; letter-spacing: 1.5vw;
text-transform: uppercase; text-transform: uppercase;
text-align: center; text-align: center;
} }
main { main {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 75vh; height: 75vh;
width: 100%; width: 100%;
background: url(https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Mountains-1412683.svg/1280px-Mountains-1412683.svg.png) background: url(https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Mountains-1412683.svg/1280px-Mountains-1412683.svg.png) no-repeat center center;
no-repeat center center; background-size: cover;
background-size: cover;
} }
.info_div { .info_div {
text-align: center; text-align: center;
margin-top: 20px; margin-top: 20px;
} }
.info_div { .info_div {
letter-spacing: 1px; letter-spacing: 1px;
} }
footer { footer {
height: 10vh; height: 10vh;
background-color: black; background-color: black;
color: white; color: white;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: -5px -5px 10px rgb(0, 0, 0, 0.3); box-shadow: -5px -5px 10px rgb(0,0,0,0.3);
} }

View File

@ -1,111 +1,108 @@
#posts > div > p > span:first-child { #posts > div > p > span:first-child {
color: #bfe7d4; color: #BFE7D4;
} }
.status { .status {
color: var(--text-color); color: var(--text-color);
} }
.self { .self {
color: var(--text-color); color: var(--text-color);
} }
#username-self { #username-self {
color: #036d19; color: #036D19;
} }
.specialtext { .specialtext {
color: yellow; color: yellow;
} }
.crossout { .crossout {
text-decoration: line-through; text-decoration: line-through;
} }
.greentext { .greentext {
color: var(--green); color: var(--green);
} }
#posts > div { #posts > div {
background-color: var(--fg-color); background-color: var(--fg-color);
padding-left: 5px; padding-left: 5px;
padding-bottom: 2px; padding-bottom: 2px;
} }
.avatar { .avatar {
margin-right: 5px; margin-right: 5px;
margin-top: 10px; margin-top: 10px;
margin-left: 5px; margin-left: 5px;
} }
.mention { .mention {
color: blue; color: blue;
} }
.user-mention { .user-mention {
color: #036d19; color: #036D19;
} }
.everyone-mention { .everyone-mention {
color: aqua; color: aqua;
} }
body { body {
background-color: var(--bg-color); background-color: var(--bg-color);
} }
textarea { textarea {
background-color: var(--bg-color); background-color: var(--bg-color);
} }
button { button {
background-color: var(--blue-ish); background-color: var(--blue-ish);
} }
.post, .post,.self,.status {
.self, padding: 1%;
.status { color: var(--text-color);
padding: 1%; width: 50%;
color: var(--text-color); margin-left: 25%;
width: 50%; margin-right: 25%;
margin-left: 25%; margin-top: 10px;
margin-right: 25%; margin-bottom: 10px;
margin-top: 10px; border-radius: 10px;
margin-bottom: 10px; overflow-wrap: break-word;
border-radius: 10px; overflow: hidden;
overflow-wrap: break-word;
overflow: hidden;
} }
.ovfl-bw { .ovfl-bw {
overflow-wrap: break-word; overflow-wrap: break-word;
overflow: hidden; overflow: hidden;
background-color: var(--bg-color); background-color: var(--bg-color);
padding: 1%; padding: 1%;
} }
#post-text, #post-text, button {
button { color: var(--text-color);
color: var(--text-color); border-radius: 5px;
border-radius: 5px; resize: none;
resize: none; overflow-wrap: break-word;
overflow-wrap: break-word; overflow: hidden;
overflow: hidden;
} }
.channelTab { .channelTab {
color: var(--text-color); color: var(--text-color);
background-color: var(--fg-color); background-color: var(--fg-color);
text-align: center; text-align: center;
float: left; float: left;
margin-left: 20px; margin-left: 20px;
margin-right: 20px; margin-right: 20px;
} }
.channel { .channel {
padding: 10px; padding: 10px;
} }
#scriptonly { #scriptonly {
margin: auto; margin: auto;
} }

View File

@ -4,5 +4,5 @@ body {
} }
.form_div { .form_div {
text-align: center; text-align: center;
} }

View File

@ -5,7 +5,7 @@ body {
} }
h1 { h1 {
color: white; color: white;
} }
button { button {
@ -13,30 +13,29 @@ button {
margin: 10px; margin: 10px;
} }
::placeholder { ::placeholder{
color: white; color: white;
} }
#bio { #bio {
color: black; color:black;
font-size: 20px; font-size: 20px;
background-color: black; background-color: black;
border: 0px solid black; border: 0px solid black;
border-radius: 7px; border-radius: 7px;
} }
.bio { .bio {
color: black; color:black;
font-size: 20px; font-size: 20px;
} }
main { main {
display: flex; display: flex;
/* align-items: center; */ /* align-items: center; */
justify-content: center; justify-content: center;
height: 75vh; height: 75vh;
width: 100%; width: 100%;
background: url(https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Mountains-1412683.svg/1280px-Mountains-1412683.svg.png) background: url(https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Mountains-1412683.svg/1280px-Mountains-1412683.svg.png) no-repeat center center;
no-repeat center center; background-size: cover;
background-size: cover;
} }

View File

@ -1,35 +0,0 @@
services:
app:
build: .
ports:
- '23080:80'
environment:
- MYSQL_HOST=db
- MYSQL_USER=ipost
- MYSQL_PASSWORD=ipost_password
- MYSQL_DATABASE=ipost
depends_on:
- db
volumes:
- ./logs:/app/logs
- ./server_config.json:/app/server_config.json
- ./cookiesecret.txt:/app/cookiesecret.txt
- ./mysql_password.txt:/app/mysql_password.txt
restart: unless-stopped
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=root_password
- MYSQL_DATABASE=ipost
- MYSQL_USER=ipost
- MYSQL_PASSWORD=ipost_password
volumes:
- mysql_data:/var/lib/mysql
- ./createSchema.sql:/docker-entrypoint-initdb.d/createSchema.sql
ports:
- '3306:3306'
restart: unless-stopped
volumes:
mysql_data:

View File

@ -1,9 +1,12 @@
import crypto from 'crypto' import crypto from "crypto";
let SHA256_cache = {} let SHA256_cache = {}
function _SHA256(str) { function _SHA256(str) {
return crypto.createHash('sha256').update(str).digest('base64') return crypto
.createHash("sha256")
.update(str)
.digest("base64");
} }
/** /**
@ -14,23 +17,25 @@ function _SHA256(str) {
* @returns {string} base64 digested hash * @returns {string} base64 digested hash
*/ */
function SHA256(str, salt, num) { function SHA256(str, salt, num) {
if (!num && num !== 0) num = 1 if (!num && num !== 0)
if (!str) return num = 1;
let identifier = _SHA256(str + salt + num.toString()) if (!str)
if (SHA256_cache[identifier] != undefined) { return;
return SHA256_cache[identifier] let identifier = _SHA256(str+salt+num.toString())
if(SHA256_cache[identifier] != undefined) {
return SHA256_cache[identifier];
} }
let ret = str let ret = str;
for (let i = 0; i < num; i++) { for (let i = 0; i < num; i++) {
ret = _SHA256(ret + salt) ret = _SHA256(ret + salt)
} }
SHA256_cache[identifier] = ret SHA256_cache[identifier] = ret;
setTimeout(() => { setTimeout(()=>{
SHA256_cache[identifier] = undefined SHA256_cache[identifier] = undefined
}, 10000) //cache for 10s },10000) //cache for 10s
return ret return ret;
} }
export { SHA256 } export { SHA256 };
export default { export default {
SHA256: SHA256, SHA256: SHA256
} };

View File

@ -1,4 +1,4 @@
import { mkdir } from 'fs' import {mkdir} from "fs"
/** /**
* makes sure that a given folder exists, if it doesn't it creates one for you * makes sure that a given folder exists, if it doesn't it creates one for you
@ -7,19 +7,23 @@ import { mkdir } from 'fs'
* @param {Function} cb callback, gives null if the folder exists, otherwise gives the error * @param {Function} cb callback, gives null if the folder exists, otherwise gives the error
* @return {undefined} see: callback * @return {undefined} see: callback
*/ */
function ensureExists(path, mask, cb) { function ensureExists(path, mask, cb) {
if (typeof mask === 'function') { if (typeof mask === 'function') { // Allow the `mask` parameter to be optional
// Allow the `mask` parameter to be optional cb = mask;
cb = mask mask = 0o744;
mask = 0o744
} }
mkdir(path, mask, function (err) { mkdir(path, mask, function (err) {
if (err) { if (err) {
if (err.code === 'EEXIST') if (err.code === 'EEXIST')
cb(null) // Ignore the error if the folder already exists cb(null); // Ignore the error if the folder already exists
else cb(err) // Something else went wrong else
} else cb(null) // Successfully created folder cb(err); // Something else went wrong
}) }
else
cb(null); // Successfully created folder
});
} }
export { ensureExists } export {
ensureExists
} ;

View File

@ -1,17 +1,14 @@
import fs from 'fs' import fs from "fs";
const config = JSON.parse(fs.readFileSync('server_config.json')) const config = JSON.parse(fs.readFileSync("server_config.json"));
/** /**
* gets ip of a request * gets ip of a request
* @param {request} req * @param {request} req
* @returns ip of the given request, after taking preferred headers into account * @returns ip of the given request, after taking preferred headers into account
*/ */
function getIP(req) { function getIP(req) {
let ip = req.socket.remoteAddress let ip = req.socket.remoteAddress;
if ( if (req.headers[config.preferred_ip_header] !== undefined && ip === config.only_prefer_when_ip)
req.headers[config.preferred_ip_header] !== undefined && ip = req.headers[config.preferred_ip_header];
ip === config.only_prefer_when_ip return ip;
)
ip = req.headers[config.preferred_ip_header]
return ip
} }
export default getIP export default getIP;

View File

@ -1,15 +1,7 @@
<ul class="navbar noselect"> <ul class="navbar noselect">
<li><a href="/">Home</a></li> <li><a href="/">Home</a></li>
<li><a href="/user" id="hide_user">Profile</a></li> <li><a href="/user" id="hide_user">Profile</a></li>
<li><a href="/posts" id="hide_posts">Posts</a></li> <li><a href="/posts" id="hide_posts">Posts</a></li>
<li><a href="/dms" id="hide_dms">DMs</a></li> <li><a href="/dms" id="hide_dms">DMs</a></li>
<li class="right"> <li class="right"><a href="/settings" id="hide_settings" class="less_padding"><img src="/images/settings_min.png" width=30 height=30 alt="settings"></a></li>
<a href="/settings" id="hide_settings" class="less_padding"
><img
src="/images/settings_min.png"
width="30"
height="30"
alt="settings"
/></a>
</li>
</ul> </ul>

View File

@ -1,7 +1,7 @@
import * as signature from 'cookie-signature' import * as signature from "cookie-signature";
import fs from 'fs' import fs from "fs";
import getIP from './getip.js' import getIP from "./getip.js";
const cookiesecret = fs.readFileSync('cookiesecret.txt').toString() const cookiesecret = fs.readFileSync("cookiesecret.txt").toString();
/** /**
* usignes a string * usignes a string
* @param {string} text text to unsign * @param {string} text text to unsign
@ -10,14 +10,14 @@ const cookiesecret = fs.readFileSync('cookiesecret.txt').toString()
* @return {string/boolean} unsigned text, or if unsigning was unsuccessful, false * @return {string/boolean} unsigned text, or if unsigning was unsuccessful, false
*/ */
function unsign(text, req, res) { function unsign(text, req, res) {
let ip = getIP(req) let ip = getIP(req);
let unsigned = signature.unsign(text, cookiesecret + ip) let unsigned = signature.unsign(text, cookiesecret + ip);
if (!unsigned) { if (!unsigned) {
unsigned = signature.unsign(text, cookiesecret) //unsafe login? unsigned = signature.unsign(text, cookiesecret); //unsafe login?
if (!unsigned) return false if(!unsigned)return false;
return unsigned return unsigned
} }
return unsigned return unsigned;
} }
/** /**
* unsignes the auth cookie of a request, also sends json response if auth cookie was invalid * unsignes the auth cookie of a request, also sends json response if auth cookie was invalid
@ -26,25 +26,26 @@ function unsign(text, req, res) {
* @return {string/boolean} unsigned cookie, or if unsigning was unsuccessful, false * @return {string/boolean} unsigned cookie, or if unsigning was unsuccessful, false
*/ */
function getunsigned(req, res) { function getunsigned(req, res) {
let cookie = req.cookies.AUTH_COOKIE let cookie = req.cookies.AUTH_COOKIE;
if (!cookie) { if (!cookie) {
res.status(403) res.status(403);
res.json({ error: 'you are not logged in! (no cookie)' }) res.json({ "error": "you are not logged in! (no cookie)" });
return return;
} }
let unsigned = unsign(cookie, req, res) let unsigned = unsign(cookie, req, res);
if (!unsigned) { if (!unsigned) {
try { try {
res.status(401) res.status(401);
res.json({ error: 'Bad auth cookie set' }) res.json({ "error": "Bad auth cookie set" });
} catch (ignored) {} //sometimes it errors, gotta debug soon }
return false catch (ignored) { } //sometimes it errors, gotta debug soon
return false;
} }
return decodeURIComponent(unsigned) return decodeURIComponent(unsigned);
} }
export { unsign } export { unsign };
export { getunsigned } export { getunsigned };
export default { export default {
unsign: unsign, unsign: unsign,
getunsigned: getunsigned, getunsigned: getunsigned
} };

View File

@ -1,24 +1,19 @@
function XOR_hex(a, b) { function XOR_hex(a, b) {
var res = '', var res = "", i = a.length, j = b.length;
i = a.length,
j = b.length
while (i-- > 0 && j-- > 0) while (i-- > 0 && j-- > 0)
res = res = (parseInt(a.charAt(i), 16) ^ parseInt(b.charAt(j), 16)).toString(16) + res;
(parseInt(a.charAt(i), 16) ^ parseInt(b.charAt(j), 16)).toString( return res;
16
) + res
return res
} }
function hexEncode(a) { function hexEncode(a) {
let hex let hex;
let result = '' let result = "";
for (let i = 0; i < a.length; i++) { for (let i = 0; i < a.length; i++) {
hex = a.charCodeAt(i).toString(16) hex = a.charCodeAt(i).toString(16);
result += ('000' + hex).slice(-4) result += ("000" + hex).slice(-4);
} }
return result return result;
} }
function xor(a, b) { function xor(a, b) {
return XOR_hex(hexEncode(a), hexEncode(b)).toString('hex') return XOR_hex(hexEncode(a), hexEncode(b)).toString("hex");
} }
export default xor export default xor;

View File

@ -9,7 +9,7 @@ const navbar = `<ul class="navbar noselect">
//<li><a href="/search" id="hide_search">Search</a></li> //<li><a href="/search" id="hide_search">Search</a></li>
function addnavbar() { function addnavbar() {
document.body.innerHTML = navbar + document.body.innerHTML document.body.innerHTML = navbar + document.body.innerHTML
} }
document.addEventListener('DOMContentLoaded', addnavbar) document.addEventListener("DOMContentLoaded", addnavbar)

View File

@ -1,31 +1,22 @@
async function setUser() { async function setUser() {
let user = await (await fetch('/api/getuser')).json() let user = await (await fetch("/api/getuser")).json()
//user["username"],user["error"] //user["username"],user["error"]
if (user['username']) if(user["username"])document.getElementById("username").innerText = `Current User: ${user["username"]}`
document.getElementById('username').innerText = if(user["error"])document.getElementById("username").innerText = `Error: ${user["error"]}`
`Current User: ${user['username']}`
if (user['error'])
document.getElementById('username').innerText =
`Error: ${user['error']}`
} }
setUser() setUser()
async function changePW() { async function changePW(){
if (window.confirm('Are you sure that you want to change your Password?')) { if(window.confirm("Are you sure that you want to change your Password?")){
let re = await ( let re = await (await post("/api/changePW",{"currentPW":document.getElementById("currentPW").value,"newPW":document.getElementById("newPW").value})).json()
await post('/api/changePW', { document.getElementById("response").innerText = re["error"] || re["success"]
currentPW: document.getElementById('currentPW').value, document.getElementById("response").style="color:green"
newPW: document.getElementById('newPW').value, if(re["error"]) {
}) document.getElementById("response").style="color:red"
).json()
document.getElementById('response').innerText =
re['error'] || re['success']
document.getElementById('response').style = 'color:green'
if (re['error']) {
document.getElementById('response').style = 'color:red'
}
document.getElementById('currentPW').value = ''
document.getElementById('newPW').value = ''
} }
document.getElementById("currentPW").value = ""
document.getElementById("newPW").value = ""
}
} }

View File

@ -1,36 +1,25 @@
async function setUser() { async function setUser() {
let user = await (await fetch('/api/getuser')).json() let user = await (await fetch("/api/getuser")).json()
//user["username"],user["error"] //user["username"],user["error"]
if (user['username']) if(user["username"])document.getElementById("username").innerText = `Current User: ${user["username"]}`
document.getElementById('username').innerText = if(user["error"])document.getElementById("username").innerText = `Error: ${user["error"]}`
`Current User: ${user['username']}`
if (user['error'])
document.getElementById('username').innerText =
`Error: ${user['error']}`
} }
setUser() setUser()
async function change() { async function change(){
if (window.confirm('Are you sure that you want to change your Username?')) { if(window.confirm("Are you sure that you want to change your Username?")){
let re = await ( let re = await (await post("/api/changeUsername",{"currentPW":document.getElementById("currentPW").value.toString(),"newUsername":document.getElementById("newUsername").value})).json()
await post('/api/changeUsername', { document.getElementById("response").innerText = re["error"] || re["success"]
currentPW: document document.getElementById("response").style="color:green"
.getElementById('currentPW') if(re["error"]) {
.value.toString(), document.getElementById("response").style="color:red"
newUsername: document.getElementById('newUsername').value,
})
).json()
document.getElementById('response').innerText =
re['error'] || re['success']
document.getElementById('response').style = 'color:green'
if (re['error']) {
document.getElementById('response').style = 'color:red'
}
document.getElementById('currentPW').value = ''
document.getElementById('newUsername').value = ''
setUser()
} }
document.getElementById("currentPW").value = ""
document.getElementById("newUsername").value = ""
setUser()
}
} }
document.getElementById('submit').addEventListener('click', change) document.getElementById("submit").addEventListener("click",change)

821
js/dms.js
View File

@ -1,8 +1,8 @@
let username let username
const wss_server = 'wss://ipost.rocks' const wss_server = "wss://ipost.rocks"
const wss_port = '443' const wss_port = "443"
const wss_URI = wss_server + ':' + wss_port const wss_URI = wss_server + ":" + wss_port
var reply_id = 0 var reply_id = 0
@ -10,51 +10,51 @@ var highest_id
var currentChannel var currentChannel
let socket = new WebSocket(wss_URI) let socket = new WebSocket(wss_URI);
socket.addEventListener('message', async function (_event) { socket.addEventListener("message", async function (_event) {
console.info('TODO: add websocket support to dms') console.info("TODO: add websocket support to dms")
// return // return
// if(wss_server === event.origin) { // if(wss_server === event.origin) {
// let data = event.data; // let data = event.data;
// let ds = JSON.parse(data) // let ds = JSON.parse(data)
// let message = ds.message // let message = ds.message
// let item = ds.data // let item = ds.data
// let username = decodeURIComponent(item.post_user_name) // let username = decodeURIComponent(item.post_user_name)
// if(message === "new_post") { // if(message === "new_post") {
// await createPost(decodeURIComponent(item.post_user_name),decodeURIComponent(item.post_text),item.post_time,item.post_special_text,highest_id+1,item.post_from_bot,item.post_reply_id,true) // await createPost(decodeURIComponent(item.post_user_name),decodeURIComponent(item.post_text),item.post_time,item.post_special_text,highest_id+1,item.post_from_bot,item.post_reply_id,true)
// if(user["username"]!==username)mainNoti(username) // if(user["username"]!==username)mainNoti(username)
// let highest_known_posts = await (await fetch(`/api/getPostsLowerThan?id=${highest_id+28}&channel=${currentChannel}`)).json() // let highest_known_posts = await (await fetch(`/api/getPostsLowerThan?id=${highest_id+28}&channel=${currentChannel}`)).json()
// for (let i = 0; i < highest_known_posts.length; i++) { // for (let i = 0; i < highest_known_posts.length; i++) {
// if(document.getElementById(highest_known_posts[i].post_id) === undefined) { // if(document.getElementById(highest_known_posts[i].post_id) === undefined) {
// main() // main()
// return; // return;
// } // }
// } // }
// highest_id++; // highest_id++;
// } // }
// } // }
}) })
var cd = true //inversed "cooldown" var cd = true //inversed "cooldown"
let encryption_keys = '' let encryption_keys = ""
function set_keys(s_key) { function set_keys(s_key) {
let key = extend(s_key, 512) let key = extend(s_key,512)
let msgkey = key.substring(0, 128) let msgkey = key.substring(0,128)
let sigkey = key.substring(129, 512) let sigkey = key.substring(129,512)
let packed = pack_keys({ let packed = pack_keys({
signkey: sigkey, signkey: sigkey,
messagekey: msgkey, messagekey: msgkey
}) })
localStorage.setItem(currentChannel + 'enc_key', packed) localStorage.setItem(currentChannel+"enc_key",packed)
encryption_keys = packed encryption_keys = packed
main() main()
} }
let last_called_postMsg = Date.now() let last_called_postMsg = Date.now()
@ -63,487 +63,424 @@ let last_called_postMsg = Date.now()
previously called postMessage previously called postMessage
*/ */
async function postMsg() { async function postMsg() {
if (Date.now() - last_called_postMsg < 100) { if((Date.now() - last_called_postMsg) < 100) {
createModal('slow down there') createModal("slow down there")
debugger debugger;
return return;
} }
last_called_postMsg = Date.now() last_called_postMsg = Date.now()
let len = document.getElementById('post-text').value.length let len = document.getElementById("post-text").value.length
if (len >= 1001) { if(len >= 1001) {
alert(`Your message cant contain more than 1000 characters! (${len})`) alert(`Your message cant contain more than 1000 characters! (${len})`)
return return
} }
if (cd && posting_id !== undefined) { if(cd && posting_id!==undefined) {
cd = false cd = false
let text = document.getElementById('post-text').value let text = document.getElementById("post-text").value
if (typeof encrypt === 'function' && encryption_keys !== '') { if(typeof encrypt === "function" && encryption_keys !== "") {
text = encrypt(text, { text = encrypt(text,{
packed: encryption_keys, packed: encryption_keys
}) })
}
let r = await post('/api/dms/post', {
message: text,
reply_id: reply_id,
receiver: currentChannel,
pid: posting_id,
})
update_pid()
if (window.location.href.split('?mention=')[1])
location.replace('/posts')
document.getElementById('post-text').value = ''
unreply()
setTimeout(function () {
cd = true
}, 200)
} else {
alert('Please wait a tiny bit before posting again')
} }
let r = await post("/api/dms/post",{"message":text,"reply_id":reply_id,"receiver":currentChannel,"pid": posting_id})
update_pid()
if(window.location.href.split("?mention=")[1])location.replace('/posts');
document.getElementById("post-text").value=""
unreply()
setTimeout(function(){
cd = true
},200)
} else {
alert("Please wait a tiny bit before posting again")
}
} }
async function update_pid() { async function update_pid() {
let r = await (await fetch('/api/dms/pid')).json() let r = await (await fetch("/api/dms/pid")).json()
console.log('new pid info: ', r) console.log("new pid info: ",r)
if (r.error) { if(r.error) {
//an error occurred //an error occurred
if (r.error === 'you cannot access the api without being logged in') { if(r.error === "you cannot access the api without being logged in") {
//account error, go to login page //account error, go to login page
location.replace('/') location.replace("/")
return return
}
//possibly more errors coming soon :tm: ?
return
} }
posting_id = r.pid
console.log('Updated pid', posting_id) //possibly more errors coming soon :tm: ?
return
}
posting_id = r.pid
console.log("Updated pid",posting_id)
} }
function spacerTextNode() { function spacerTextNode() {
return document.createTextNode(' | ') return document.createTextNode(" | ")
} }
const user_cache = {} const user_cache = {}
async function getavatar(username) { async function getavatar(username) {
let user = user_cache[username] let user = user_cache[username]
if (user === undefined) { if(user === undefined) {
user = ( user = (await (await fetch("/api/getotheruser?user="+encodeURIComponent(username))).json())["avatar"]
await ( if(user) {
await fetch( user = "/avatars/"+user
'/api/getotheruser?user=' + encodeURIComponent(username)
)
).json()
)['avatar']
if (user) {
user = '/avatars/' + user
} else {
user = '/images/default_avatar.png'
}
user_cache[username] = user
}
return user
}
async function reply_link_clicked(reply_channel, reply_id) {
console.log('clicked link')
if (reply_channel !== currentChannel) {
console.log('reply is in another channel')
switchChannel(reply_channel)
console.log('switched channel')
await main()
console.log('loaded new messages')
let replied_msg = document.getElementById(reply_id)
if (replied_msg) {
console.log('found element')
replied_msg.scrollIntoView()
}
} else { } else {
let replied_msg = document.getElementById(reply_id) user = "/images/default_avatar.png"
if (replied_msg) {
console.log('found element')
replied_msg.scrollIntoView()
}
} }
user_cache[username]=user
}
return user
} }
async function createPost( async function reply_link_clicked(reply_channel,reply_id) {
username, console.log("clicked link")
text, if(reply_channel !== currentChannel) {
time, console.log("reply is in another channel")
specialtext, switchChannel(reply_channel)
postid, console.log("switched channel")
isbot, await main()
reply_id, console.log("loaded new messages")
add_on_top let replied_msg = document.getElementById(reply_id)
) { if(replied_msg) {
if (!specialtext) specialtext = '' console.log("found element")
const newDiv = document.createElement('div') replied_msg.scrollIntoView()
const newP = document.createElement('p')
const newA = document.createElement('a')
const newSpan2 = document.createElement('span')
const newSpan3 = document.createElement('span')
const avatar = document.createElement('img')
const boticon = document.createElement('img')
const replyDiv = document.createElement('div')
const replyA = document.createElement('a')
const replyAvatar = document.createElement('img')
const replySpan = document.createElement('span')
const replyBr = document.createElement('br')
boticon.src = '/images/bot.png'
boticon.height = 25
boticon.width = 25
boticon.classList.add('boticon')
const newUsername = document.createTextNode(username)
let timedate = new Date(time)
time = timedate
time = time.toString()
time = time.split(' ')
time =
time[0] + ' ' + time[1] + ' ' + time[2] + ' ' + time[3] + ' ' + time[4]
if (
timedate ===
'Thu Jan 01 1970 01:00:00 GMT+0100 (Central European Standard Time)'
)
time = 'unknown time'
const newTime = document.createTextNode(time)
const newSpecialText = document.createTextNode(specialtext)
newDiv.classList.add('post')
newSpan3.classList.add('specialtext')
avatar.width = 25
avatar.height = 25
avatar.classList.add('avatar')
avatar.src = await getavatar(username)
newA.appendChild(avatar)
newA.appendChild(newUsername)
newA.href = `/users/${username}`
newSpan2.appendChild(newTime)
newSpan3.appendChild(newSpecialText)
newP.appendChild(newA)
newP.appendChild(spacerTextNode())
newP.appendChild(newSpan2)
if (specialtext !== '') newP.appendChild(spacerTextNode())
newP.appendChild(newSpan3)
if (isbot === 1) {
newP.appendChild(spacerTextNode())
newP.appendChild(boticon)
} }
newP.appendChild(spacerTextNode()) } else {
// |\>.</| let replied_msg = document.getElementById(reply_id)
newP.innerHTML += `<button onclick="reply(${postid})">Reply to this Post</button>` if(replied_msg) {
console.log("found element")
if (reply_id !== 0) { replied_msg.scrollIntoView()
try {
const reply_obj = await (
await fetch(`/api/dms/getDM?id=${reply_id}`)
).json()
const reply_username = decodeURIComponent(reply_obj.dms_user_name)
const reply_username_text = document.createTextNode(reply_username)
let reply_text = decodeURIComponent(reply_obj.dms_text)
const reply_channel = reply_obj.dms_receiver
replyAvatar.width = 10
replyAvatar.height = 10
replyAvatar.classList.add('avatar')
replyAvatar.src = await getavatar(reply_username)
replyA.appendChild(replyAvatar)
replyA.appendChild(reply_username_text)
replyA.appendChild(spacerTextNode())
if (typeof decrypt === 'function' && encryption_keys !== '') {
reply_text = decrypt(reply_text, {
packed: encryption_keys,
}).msg
}
replyA.innerHTML += filterReply(
reply_text.replace('\n', ' ').substring(0, 20)
)
replyA.appendChild(replyBr)
replyA.classList.add('no-link-style')
// async function onclick(event) {
// event.preventDefault()
// }
// replyDiv.onclick = function() {
// reply_link_clicked(reply_channel, reply_id)
// }
replyDiv.appendChild(replyA)
newDiv.appendChild(replyDiv)
replyDiv.outerHTML = replyDiv.outerHTML.replace(
/\>/im,
` onclick="reply_link_clicked('${reply_channel}',${reply_id})" \>`
)
} catch (ignored) {
console.log(ignored)
}
}
if (typeof decrypt === 'function' && encryption_keys !== '') {
text = decrypt(text, { packed: encryption_keys }).msg
}
newDiv.appendChild(newP)
newDiv.innerHTML += filterPost(text)
newDiv.id = postid
let posts_div = document.getElementById('posts')
if (add_on_top) {
posts_div.insertBefore(newDiv, posts_div.children[0])
} else {
posts_div.appendChild(newDiv)
} }
}
} }
async function main() { async function createPost(username,text,time,specialtext,postid,isbot,reply_id,add_on_top) {
if (!user) { if(!specialtext)specialtext=""
user = await (await fetch('/api/getuser')).json() const newDiv = document.createElement("div");
username = user.username const newP = document.createElement("p");
if (!username) { const newA = document.createElement("a");
user = undefined const newSpan2 = document.createElement("span");
document.getElementById('noaccount').style = '' const newSpan3 = document.createElement("span");
document.getElementById('loading').style = 'display:none;' const avatar = document.createElement("img");
console.log('no account') const boticon = document.createElement("img");
return
} const replyDiv = document.createElement("div");
const replyA = document.createElement("a");
const replyAvatar = document.createElement("img");
const replySpan = document.createElement("span");
const replyBr = document.createElement("br");
boticon.src = "/images/bot.png"
boticon.height = 25
boticon.width = 25
boticon.classList.add("boticon")
const newUsername = document.createTextNode(username);
let timedate = new Date(time)
time = timedate
time = time.toString()
time = time.split(" ")
time = time[0] + " " + time[1] + " " + time[2] + " " + time[3] + " " + time[4]
if(timedate==="Thu Jan 01 1970 01:00:00 GMT+0100 (Central European Standard Time)")time="unknown time"
const newTime = document.createTextNode(time)
const newSpecialText = document.createTextNode(specialtext)
newDiv.classList.add("post");
newSpan3.classList.add("specialtext")
avatar.width=25;
avatar.height=25;
avatar.classList.add("avatar")
avatar.src = await getavatar(username)
newA.appendChild(avatar)
newA.appendChild(newUsername)
newA.href = `/users/${username}`
newSpan2.appendChild(newTime)
newSpan3.appendChild(newSpecialText)
newP.appendChild(newA)
newP.appendChild(spacerTextNode())
newP.appendChild(newSpan2)
if(specialtext !== "")newP.appendChild(spacerTextNode())
newP.appendChild(newSpan3)
if(isbot===1){
newP.appendChild(spacerTextNode())
newP.appendChild(boticon)
}
newP.appendChild(spacerTextNode())
// |\>.</|
newP.innerHTML += `<button onclick="reply(${postid})">Reply to this Post</button>`
if(reply_id !== 0) {
try {
const reply_obj = await (await fetch(`/api/dms/getDM?id=${reply_id}`)).json()
const reply_username = decodeURIComponent(reply_obj.dms_user_name)
const reply_username_text = document.createTextNode(reply_username)
let reply_text = decodeURIComponent(reply_obj.dms_text)
const reply_channel = reply_obj.dms_receiver
replyAvatar.width=10;
replyAvatar.height=10;
replyAvatar.classList.add("avatar")
replyAvatar.src = await getavatar(reply_username)
replyA.appendChild(replyAvatar)
replyA.appendChild(reply_username_text)
replyA.appendChild(spacerTextNode())
if(typeof decrypt === "function" && encryption_keys !== "") {
reply_text = decrypt(reply_text,{packed:encryption_keys}).msg
}
replyA.innerHTML += filterReply(reply_text.replace("\n"," ").substring(0,20))
replyA.appendChild(replyBr)
replyA.classList.add("no-link-style")
// async function onclick(event) {
// event.preventDefault()
// }
// replyDiv.onclick = function() {
// reply_link_clicked(reply_channel, reply_id)
// }
replyDiv.appendChild(replyA)
newDiv.appendChild(replyDiv)
replyDiv.outerHTML = replyDiv.outerHTML.replace(/\>/im,` onclick="reply_link_clicked('${reply_channel}',${reply_id})" \>`)
} catch (ignored) {
console.log(ignored)
} }
}
if(typeof decrypt === "function" && encryption_keys !== "") {
text = decrypt(text,{packed:encryption_keys}).msg
}
newDiv.appendChild(newP)
newDiv.innerHTML += filterPost(text)
newDiv.id = postid
let posts_div = document.getElementById("posts")
if(add_on_top) {
posts_div.insertBefore(newDiv, posts_div.children[0]);
} else {
posts_div.appendChild(newDiv)
}
}
async function main(){
if(!user){
user = await (await fetch("/api/getuser")).json()
username = user.username username = user.username
document.getElementById('username-self').innerText = username if(!username){
user = undefined
document.getElementById("noaccount").style=""
document.getElementById("loading").style="display:none;"
console.log("no account");
return;
}
}
username = user.username
document.getElementById("username-self").innerText = username
let all_posts = await ( let all_posts = await (await fetch(`/api/getPersonalPosts?otherperson=${currentChannel}`)).json()
await fetch(`/api/getPersonalPosts?otherperson=${currentChannel}`) if(!all_posts){
).json() document.getElementById("loading").style="display:none;"
if (!all_posts) { document.getElementById("scriptonly").style = ""
document.getElementById('loading').style = 'display:none;' };
document.getElementById('scriptonly').style = '' document.getElementById("posts").innerHTML = ""
} if(all_posts.length <= 0) {
document.getElementById('posts').innerHTML = '' document.getElementById("loading").style="display:none;"
if (all_posts.length <= 0) { document.getElementById("scriptonly").style = ""
document.getElementById('loading').style = 'display:none;' //TODO: empty page
document.getElementById('scriptonly').style = '' return
//TODO: empty page }
return highest_id = all_posts[0].post_id
} for(i in all_posts) {
highest_id = all_posts[0].post_id let item = all_posts[i]
for (i in all_posts) { await createPost(decodeURIComponent(item.dms_user_name),decodeURIComponent(item.dms_text),item.dms_time,item.dms_special_text,item.dms_id,item.dms_from_bot,item.dms_reply_id,false)
let item = all_posts[i] }
await createPost(
decodeURIComponent(item.dms_user_name),
decodeURIComponent(item.dms_text),
item.dms_time,
item.dms_special_text,
item.dms_id,
item.dms_from_bot,
item.dms_reply_id,
false
)
}
let links = document.getElementsByClassName('insertedlink') let links = document.getElementsByClassName("insertedlink")
for (let i = 0; i < links.length; i++) { for (let i = 0; i < links.length; i++) {
links[i].innerText = links[i].innerText.split('\/\/')[1].split('\/')[0] links[i].innerText = links[i].innerText.split("\/\/")[1].split("\/")[0]
} }
let mentions = document.getElementsByClassName('mention') let mentions = document.getElementsByClassName("mention")
for (let i = 0; i < mentions.length; i++) { for (let i = 0; i < mentions.length; i++) {
if ( if(mentions[i]!==undefined && mentions[i].innerText === "@"+username) {
mentions[i] !== undefined && mentions[i].classList.add("user-mention");
mentions[i].innerText === '@' + username mentions[i].classList.remove("mention");
) { i--;
mentions[i].classList.add('user-mention')
mentions[i].classList.remove('mention')
i--
}
} }
}
document.getElementById('loading').style = 'display:none;' document.getElementById("loading").style="display:none;"
document.getElementById('scriptonly').style = '' document.getElementById("scriptonly").style = ""
} }
async function reply(postid) { async function reply(postid) {
let post = await (await fetch('/api/dms/getDM?id=' + postid)).json() let post = await(await fetch("/api/dms/getDM?id="+postid)).json()
let username = post.dms_user_name let username = post.dms_user_name
let posttext = post.dms_text let posttext = post.dms_text
document.getElementById('reply').style = '' document.getElementById("reply").style = ""
document.getElementById('reply_username').innerText = document.getElementById("reply_username").innerText = decodeURIComponent(username)
decodeURIComponent(username)
posttext = decodeURIComponent(posttext) posttext = decodeURIComponent(posttext)
if (typeof decrypt === 'function' && encryption_keys !== '') { if(typeof decrypt === "function" && encryption_keys !== "") {
posttext = decrypt(posttext, { packed: encryption_keys }).msg posttext = decrypt(posttext,{packed:encryption_keys}).msg
} }
document.getElementById('reply_text').innerHTML = filterPost(posttext) document.getElementById("reply_text").innerHTML = filterPost(posttext)
reply_id = postid reply_id = postid
} }
function unreply() { function unreply() {
document.getElementById('reply').style = 'display:none;' document.getElementById("reply").style = "display:none;"
reply_id = 0 reply_id = 0
} }
var cansendNoti = false var cansendNoti = false
function askNotiPerms() { function askNotiPerms() {
return Notification.requestPermission() return Notification.requestPermission()
} }
async function firstAsk() { async function firstAsk() {
if ( if(Notification.permission === 'denied' || Notification.permission === 'default') {
Notification.permission === 'denied' || await askNotiPerms()
Notification.permission === 'default' }
) {
await askNotiPerms()
}
} }
async function mainNoti(user) { async function mainNoti(user) {
if (
Notification.permission === 'denied' || if(Notification.permission === 'denied' || Notification.permission === 'default') {
Notification.permission === 'default' await askNotiPerms()
) { } else {
await askNotiPerms() if(cansendNoti) {
} else { let notification = new Notification('IPost', { body: "new dm from " + user , tag: "new_post"});
if (cansendNoti) { notification = await notification
let notification = new Notification('IPost', { notification.addEventListener("click",function(){
body: 'new dm from ' + user, notification.close()
tag: 'new_post', })
}) console.log(notification);
notification = await notification
notification.addEventListener('click', function () {
notification.close()
})
console.log(notification)
}
} }
}
} }
document.addEventListener('visibilitychange', function () { document.addEventListener("visibilitychange", function() {
//cansendNoti = document.visibilityState !== 'visible' //cansendNoti = document.visibilityState !== 'visible'
if (document.visibilityState === 'visible') { if (document.visibilityState === 'visible') {
cansendNoti = false cansendNoti = false
} else { } else {
cansendNoti = true cansendNoti = true
} }
}) });
function switchChannel(channelname) { function switchChannel(channelname) {
sessionStorage.setItem('lastdm', channelname) sessionStorage.setItem("lastdm", channelname);
currentChannel = channelname currentChannel = channelname
if (localStorage.getItem(currentChannel + 'enc_key') !== null) { if(localStorage.getItem(currentChannel+"enc_key")!==null) {
encryption_keys = localStorage.getItem(currentChannel + 'enc_key') encryption_keys = localStorage.getItem(currentChannel+"enc_key")
} else { } else {
encryption_keys = '' encryption_keys = ""
} }
try { try {
socket.send(JSON.stringify({ id: 'switchChannel', data: channelname })) socket.send(JSON.stringify({"id":"switchChannel","data":channelname}))
} catch (err) { } catch(err) {
console.error(err) console.error(err)
} }
} }
function removeDuplicates(a) { function removeDuplicates(a) {
let prims = { boolean: {}, number: {}, string: {} }, let prims = {"boolean":{}, "number":{}, "string":{}}, objs = [];
objs = []
return a.filter(function (item) { return a.filter(function(item) {
let type = typeof item let type = typeof item;
if (type in prims) if(type in prims)
return Object.prototype.hasOwnProperty.call(prims[type], item) return Object.prototype.hasOwnProperty.call(prims[type], item) ? false : (prims[type][item] = true);
? false else
: (prims[type][item] = true) return objs.indexOf(item) >= 0 ? false : objs.push(item);
else return objs.indexOf(item) >= 0 ? false : objs.push(item) });
})
} }
function createChannel(channelname, tab) { function createChannel(channelname,tab) {
channelname = decodeURIComponent(channelname) channelname = decodeURIComponent(channelname)
let channelp = document.createElement('p') let channelp = document.createElement("p")
channelp.classList.add('channel') channelp.classList.add("channel")
channelp.id = channelname channelp.id = channelname
let textnode = document.createTextNode(channelname) let textnode = document.createTextNode(channelname)
channelp.appendChild(textnode) channelp.appendChild(textnode)
channelp.addEventListener('click', function () { channelp.addEventListener("click", function(){
switchChannel(channelname) switchChannel(channelname)
main() main()
unreply() unreply()
}) })
tab.appendChild(channelp) tab.appendChild(channelp)
} }
async function loadChannels() { async function loadChannels() {
// <!-- <p class="channel">- Channel Name -</p> --> // <!-- <p class="channel">- Channel Name -</p> -->
let dms = await (await fetch('/api/dms/conversations')).json() let dms = await (await fetch("/api/dms/conversations")).json()
let channels = [] let channels = []
for (let dm of dms) { for(let dm of dms) {
if (dm.dms_user_name === username) { if(dm.dms_user_name === username) {
channels[channels.length] = dm.dms_receiver channels[channels.length] = dm.dms_receiver
} else { } else {
channels[channels.length] = dm.dms_user_name channels[channels.length] = dm.dms_user_name
} }
} }
channels = removeDuplicates(channels) channels = removeDuplicates(channels)
let tab = document.getElementById('channelTab') let tab = document.getElementById("channelTab")
tab.innerHTML = '' tab.innerHTML = ""
for (let i = 0; i < channels.length; i++) { for (let i = 0; i < channels.length; i++) {
if (channels[i] === '') continue if(channels[i]==="")continue;
createChannel(channels[i], tab) createChannel(channels[i],tab)
} }
} }
function init() { function init() {
switchChannel(sessionStorage.getItem('lastdm') || 'none')
setInterval(update_pid, 30000) switchChannel(sessionStorage.getItem("lastdm") || "none")
update_pid()
main() setInterval(update_pid,30000)
firstAsk() update_pid()
loadChannels() main()
firstAsk()
loadChannels()
} }
async function clickPress(event) { async function clickPress(event) {
if (event.key === 'Enter') { if (event.key === "Enter") {
user = await ( user = (await (await fetch("/api/getotheruser?user="+encodeURIComponent(document.getElementById("Username_input").value))).json())
await fetch( if(user.username === undefined) {
'/api/getotheruser?user=' + alert("invalid username entered")
encodeURIComponent( return
document.getElementById('Username_input').value } else {
) if(document.getElementById(user.username) === undefined) {
) let tab = document.getElementById("channelTab")
).json() createChannel(encodeURIComponent(user.username),tab)
if (user.username === undefined) { }
alert('invalid username entered') document.getElementById(user.username).click()
return document.getElementById("Username_input").value = ""
} else { }
if (document.getElementById(user.username) === undefined) { }
let tab = document.getElementById('channelTab')
createChannel(encodeURIComponent(user.username), tab)
}
document.getElementById(user.username).click()
document.getElementById('Username_input').value = ''
}
}
} }
init() init()

View File

@ -1,82 +1,18 @@
// skipqc // skipqc
var sha256 = function a(b) { var sha256=function a(b){function c(a,b){return a>>>b|a<<32-b}for(var d,e,f=Math.pow,g=f(2,32),h="length",i="",j=[],k=8*b[h],l=a.h=a.h||[],m=a.k=a.k||[],n=m[h],o={},p=2;n<64;p++)if(!o[p]){for(d=0;d<313;d+=p)o[d]=p;l[n]=f(p,.5)*g|0,m[n++]=f(p,1/3)*g|0}for(b+="\x80";b[h]%64-56;)b+="\x00";for(d=0;d<b[h];d++){if(e=b.charCodeAt(d),e>>8)return;j[d>>2]|=e<<(3-d)%4*8}for(j[j[h]]=k/g|0,j[j[h]]=k,e=0;e<j[h];){var q=j.slice(e,e+=16),r=l;for(l=l.slice(0,8),d=0;d<64;d++){var s=q[d-15],t=q[d-2],u=l[0],v=l[4],w=l[7]+(c(v,6)^c(v,11)^c(v,25))+(v&l[5]^~v&l[6])+m[d]+(q[d]=d<16?q[d]:q[d-16]+(c(s,7)^c(s,18)^s>>>3)+q[d-7]+(c(t,17)^c(t,19)^t>>>10)|0),x=(c(u,2)^c(u,13)^c(u,22))+(u&l[1]^u&l[2]^l[1]&l[2]);l=[w+x|0].concat(l),l[4]=l[4]+w|0}for(d=0;d<8;d++)l[d]=l[d]+r[d]|0}for(d=0;d<8;d++)for(e=3;e+1;e--){var y=l[d]>>8*e&255;i+=(y<16?0:"")+y.toString(16)}return i};
function c(a, b) {
return (a >>> b) | (a << (32 - b))
}
for (
var d,
e,
f = Math.pow,
g = f(2, 32),
h = 'length',
i = '',
j = [],
k = 8 * b[h],
l = (a.h = a.h || []),
m = (a.k = a.k || []),
n = m[h],
o = {},
p = 2;
n < 64;
p++
)
if (!o[p]) {
for (d = 0; d < 313; d += p) o[d] = p
;(l[n] = (f(p, 0.5) * g) | 0), (m[n++] = (f(p, 1 / 3) * g) | 0)
}
for (b += '\x80'; (b[h] % 64) - 56; ) b += '\x00'
for (d = 0; d < b[h]; d++) {
if (((e = b.charCodeAt(d)), e >> 8)) return
j[d >> 2] |= e << (((3 - d) % 4) * 8)
}
for (j[j[h]] = (k / g) | 0, j[j[h]] = k, e = 0; e < j[h]; ) {
var q = j.slice(e, (e += 16)),
r = l
for (l = l.slice(0, 8), d = 0; d < 64; d++) {
var s = q[d - 15],
t = q[d - 2],
u = l[0],
v = l[4],
w =
l[7] +
(c(v, 6) ^ c(v, 11) ^ c(v, 25)) +
((v & l[5]) ^ (~v & l[6])) +
m[d] +
(q[d] =
d < 16
? q[d]
: (q[d - 16] +
(c(s, 7) ^ c(s, 18) ^ (s >>> 3)) +
q[d - 7] +
(c(t, 17) ^ c(t, 19) ^ (t >>> 10))) |
0),
x =
(c(u, 2) ^ c(u, 13) ^ c(u, 22)) +
((u & l[1]) ^ (u & l[2]) ^ (l[1] & l[2]))
;(l = [(w + x) | 0].concat(l)), (l[4] = (l[4] + w) | 0)
}
for (d = 0; d < 8; d++) l[d] = (l[d] + r[d]) | 0
}
for (d = 0; d < 8; d++)
for (e = 3; e + 1; e--) {
var y = (l[d] >> (8 * e)) & 255
i += (y < 16 ? 0 : '') + y.toString(16)
}
return i
}
function hash(str, salt, num) { function hash(str,salt,num) {
if (!num && num !== 0) num = 1 if(!num && num!==0)num=1;
if (!str) return if(!str)return;
if (!salt) salt = '' if(!salt)salt=""
let ret = str let ret = str;
for (let i = 0; i < num; i++) { for (let i = 0; i < num; i++) {
ret = sha256(ret + salt) ret = sha256(ret+salt)
} }
return ret return ret;
} }
function extend(key, len) { function extend(key,len) {
let temp = [] let temp = []
let out = [] let out = []
@ -84,16 +20,18 @@ function extend(key, len) {
let hashes = 0 let hashes = 0
for (let i = 0; i < len / 64; i++) { for(let i=0;i<len/64;i++) {
temp[0] = hash(key, '', ++hashes)
temp[1] = hash(key, '', ++hashes) temp[0] = hash(key,"",++hashes)
temp[2] = hash(key, '', ++hashes) temp[1] = hash(key,"",++hashes)
out[out.length] = hash(temp[0] + temp[1], temp[2], ++hashes) temp[2] = hash(key,"",++hashes)
out[out.length] = hash(temp[0]+temp[1],temp[2],++hashes)
temp = [] temp = []
} }
return out.join('').substring(0, len) return out.join("").substring(0,len)
} }

View File

@ -1,63 +1,64 @@
/** /**
* Copyright (C) 2017-present by Andrea Giammarchi - @WebReflection * Copyright (C) 2017-present by Andrea Giammarchi - @WebReflection
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
//https://github.com/WebReflection/html-escaper //https://github.com/WebReflection/html-escaper
const { replace } = '' const {replace} = '';
const es = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/gi const es = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/gi;
const ca = /[&<>'"]/g const ca = /[&<>'"]/g;
const esca = { const esca = {
'&': '&amp;', '&': '&amp;',
'<': '&lt;', '<': '&lt;',
'>': '&gt;', '>': '&gt;',
"'": '&#39;', "'": '&#39;',
'"': '&quot;', '"': '&quot;'
} };
const pe = (m) => esca[m] const pe = m => esca[m];
const escape = es => replace.call(es, ca, pe);
const htmlesc = es => replace.call(es, ca, pe);
const escape = (es) => replace.call(es, ca, pe)
const htmlesc = (es) => replace.call(es, ca, pe)
const unes = { const unes = {
'&amp;': '&', '&amp;': '&',
'&#38;': '&', '&#38;': '&',
'&lt;': '<', '&lt;': '<',
'&#60;': '<', '&#60;': '<',
'&gt;': '>', '&gt;': '>',
'&#62;': '>', '&#62;': '>',
'&apos;': "'", '&apos;': "'",
'&#39;': "'", '&#39;': "'",
'&quot;': '"', '&quot;': '"',
'&#34;': '"', '&#34;': '"'
} };
const cape = (m) => unes[m] const cape = m => unes[m];
const unescape = (un) => replace.call(un, es, cape) const unescape = un => replace.call(un, es, cape);
function escape_special(str) { function escape_special(str) {
return str.replace(/\\/g, '\\\\').replace(/`/g, '\\`') return str.replace(/\\/g,"\\\\").replace(/`/g,"\\`")
} }
function unescape_special(str) { function unescape_special(str) {
return str.replace(/\\\\/g, '\\').replace(/\\`/, '`') return str.replace(/\\\\/g,"\\").replace(/\\`/,"`")
} }

View File

@ -1,7 +1 @@
window.post = function (url, data) { window.post = function(url, data) {return fetch(url, {method: "POST", headers: {'Content-Type': 'application/json'}, body: JSON.stringify(data)});}
return fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
}

View File

@ -1,12 +1,12 @@
window.addEventListener('load', async function () { window.addEventListener("load",async function(){
let data = await (await fetch('/api/getuser')).json() let data = await(await fetch("/api/getuser")).json()
if (data['username'] !== undefined) { if(data["username"] !== undefined) {
document.getElementById('HasAccount').style = '' document.getElementById("HasAccount").style=""
} else { } else {
document.getElementById('NoAccount').style = '' document.getElementById("NoAccount").style=""
document.getElementById('hide_user').style = 'display:none;' document.getElementById("hide_user").style="display:none;"
document.getElementById('hide_posts').style = 'display:none;' document.getElementById("hide_posts").style="display:none;"
document.getElementById('hide_dms').style = 'display:none;' document.getElementById("hide_dms").style="display:none;"
//document.getElementById("hide_search").style="display:none;" //document.getElementById("hide_search").style="display:none;"
} }
}) })

View File

@ -1,21 +1,21 @@
async function login() { async function login() {
let r = await post('/login', { let r = (await post("/login",{
user: document.getElementById('user').value, user: document.getElementById("user").value,
pass: document.getElementById('pass').value, pass: document.getElementById("pass").value,
r: REDIRECT_URL, r: REDIRECT_URL
}) }))
if (!r.url.endsWith('/user') && !r.url.endsWith(REDIRECT_URL)) { if(!r.url.endsWith("/user") && !r.url.endsWith(REDIRECT_URL)) {
document.getElementById('pass').value = '' document.getElementById("pass").value = ""
console.error('login failed') console.error("login failed")
alert('Login failed, please make sure you have the right password') alert("Login failed, please make sure you have the right password")
return return;
} }
window.location = REDIRECT_URL || '/user' window.location = REDIRECT_URL || "/user"
} }
let passfield = document.getElementById('pass') let passfield = document.getElementById("pass")
function passkeydown(e) { function passkeydown(e) {
if (e.code === 'Enter') { if(e.code === "Enter") {
login() login()
} }
} }

View File

@ -1,56 +1,45 @@
const urlregex = const urlregex = /(([a-z]+:\/\/)(([a-z0-9\-]+\.)+([a-z]{2}|aero|arpa|app|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal|tk|rocks|ga|to))(:[0-9]{1,5})?(\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(\?[a-z0-9+_\-\.%=&amp;]*)?)?(#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(\s+|$)/gi
/(([a-z]+:\/\/)(([a-z0-9\-]+\.)+([a-z]{2}|aero|arpa|app|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal|tk|rocks|ga|to))(:[0-9]{1,5})?(\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(\?[a-z0-9+_\-\.%=&amp;]*)?)?(#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(\s+|$)/gi
function urlify(text) { function urlify(text) {
return text.replace( return text.replace(urlregex,'<a href="$1" target="_blank" class="insertedlink">$1</a> ')
urlregex,
'<a href="$1" target="_blank" class="insertedlink">$1</a> '
)
} }
const newlregex = /(\n)/gi const newlregex = /(\n)/gi
function newlineify(text) { function newlineify(text) {
return text.replace(newlregex, ' <br>') return text.replace(newlregex,' <br>')
} }
const crossregex = /~([^~]*)~/gi const crossregex = /~([^~]*)~/gi
function crossout(text) { function crossout(text) {
return text.replace(crossregex, '<span class="crossout">$1</span>') return text.replace(crossregex,'<span class="crossout">$1</span>')
} }
const italicregex = /\*([^\*]*)\*/gi const italicregex = /\*([^\*]*)\*/gi
function italicify(text) { function italicify(text) {
return text.replace(italicregex, '<i>$1</i> ') return text.replace(italicregex,'<i>$1</i> ')
} }
const boldregex = /\*\*([^\*]*)\*\*/gi const boldregex = /\*\*([^\*]*)\*\*/gi
function boldify(text) { function boldify(text) {
return text.replace(boldregex, '<b>$1</b> ') return text.replace(boldregex,'<b>$1</b> ')
} }
const mentionregex = /@([^\s]*)/gi const mentionregex = /@([^\s]*)/gi
function filterMentions(text) { function filterMentions(text) {
return text.replace( return text.replace(mentionregex,`<span><a href="/users/$1" class="mention">$1</a></span> `)
mentionregex,
`<span><a href="/users/$1" class="mention">$1</a></span> `
)
} }
const emojiregex = /:([^:\s]*):/gi const emojiregex = /:([^:\s]*):/gi
function emojify(text) { function emojify(text) {
return text.replace( return text.replace(emojiregex,"<img class='emoji' src='/images/emoji/$1.png' alt=':$1:' title=':$1:' height=20/>")
emojiregex,
"<img class='emoji' src='/images/emoji/$1.png' alt=':$1:' title=':$1:' height=20/>"
)
} }
function unemojify(text) { function unemojify(text){
text = text.replace(/\u{1F5FF}/gu, ':moyai:') text = text.replace(/\u{1F5FF}/gu,":moyai:")
text = text.replace(/\u{1F440}/gu, ':eyes:') text = text.replace(/\u{1F440}/gu,":eyes:")
return text return text
} }
const allregex = const allregex = /(```([^```]*)```)|(\n)|(~([^~]*)~)|(\*\*([^\*]*)\*\*)|(\*([^\*]*)\*)|(@[^\s]*)|(:([^:\s]*):)/gi
/(```([^```]*)```)|(\n)|(~([^~]*)~)|(\*\*([^\*]*)\*\*)|(\*([^\*]*)\*)|(@[^\s]*)|(:([^:\s]*):)/gi
const cdblregex = /```([^```]*)```/gi const cdblregex = /```([^```]*)```/gi
@ -59,28 +48,29 @@ const cdblregex = /```([^```]*)```/gi
* @param {string} text text to filter/format * @param {string} text text to filter/format
* @return {string} html that represents the filtered text * @return {string} html that represents the filtered text
*/ */
function filterPost(text) { function filterPost(text){
text = unemojify(text) text = unemojify(text)
let result = htmlesc(text).replace(allregex, function (match) { let result = htmlesc(text).replace(allregex, function (match) {
let out = match let out = match
if (cdblregex.test(match)) { if(cdblregex.test(match)) {
let paddlen = 3 let paddlen = 3
out = out.substring(paddlen, out.length - paddlen).trim() + '\n' out = out.substring(paddlen,out.length-paddlen).trim()+"\n"
out = newlineify(out) out = newlineify(out)
return `<div class="ovfl-bw"><code>${out}</code></div>` return `<div class="ovfl-bw"><code>${out}</code></div>`
} }
out = newlineify(out) out = newlineify(out)
out = urlify(out) out = urlify(out)
out = emojify(out) out = emojify(out)
out = filterMentions(out) out = filterMentions(out)
out = crossout(out) out = crossout(out)
out = boldify(out) out = boldify(out)
out = italicify(out) out = italicify(out)
return out return out
})
return result });
return result
} }
/** /**
@ -89,12 +79,12 @@ function filterPost(text) {
* @return {string} html that represents the filtered text * @return {string} html that represents the filtered text
*/ */
function filterReply(text) { function filterReply(text) {
text = htmlesc(text) text = htmlesc(text)
text = newlineify(text) text = newlineify(text)
text = urlify(text) text = urlify(text)
text = crossout(text) text = crossout(text)
text = boldify(text) text = boldify(text)
text = italicify(text) text = italicify(text)
return text return text
} }

View File

@ -1,28 +1,28 @@
function createModal(text, renderAsHTML = false) { function createModal(text,renderAsHTML=false) {
if (!document.getElementById('modal')) { if(!document.getElementById("modal")) {
const shade = document.createElement('div') const shade = document.createElement("div")
shade.id = 'modal-shade' shade.id = "modal-shade"
const m = document.createElement('div') const m = document.createElement("div")
m.id = 'modal' m.id = "modal"
const close = document.createElement('button') const close = document.createElement("button")
close.id = 'modal-close-button' close.id = "modal-close-button"
close.innerText = 'Close' close.innerText = "Close"
close.onclick = function () { close.onclick = function() {
m.style.display = shade.style.display = 'none' m.style.display = shade.style.display = 'none';
} }
const textdiv = document.createElement('div') const textdiv = document.createElement("div")
textdiv.id = 'modal-text-div' textdiv.id = "modal-text-div"
m.appendChild(textdiv) m.appendChild(textdiv)
m.appendChild(close) m.appendChild(close)
document.body.insertBefore(m, document.body.children[0]) document.body.insertBefore(m,document.body.children[0])
document.body.insertBefore(shade, document.body.children[0]) document.body.insertBefore(shade,document.body.children[0])
} }
const currentModal = document.getElementById('modal') const currentModal = document.getElementById("modal")
const shade = document.getElementById('modal-shade') const shade = document.getElementById("modal-shade")
if (renderAsHTML) { if(renderAsHTML) {
document.getElementById('modal-text-div').innerHTML = text document.getElementById("modal-text-div").innerHTML = text
} else { } else {
document.getElementById('modal-text-div').innerText = text document.getElementById("modal-text-div").innerText = text
} }
currentModal.style.display = shade.style.display = 'block' currentModal.style.display = shade.style.display = "block"
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +1,37 @@
async function register() { async function register() {
if (document.getElementById('pass').value.length < 10) { if(document.getElementById("pass").value.length < 10) {
alert('Password has to be at least 10 characters long') alert("Password has to be at least 10 characters long")
return return;
} }
if (document.getElementById('user').value.length > 25) { if(document.getElementById("user").value.length > 25) {
alert('Username is too long!') alert("Username is too long!")
return return;
} }
if (document.getElementById('user').value.search('@') !== -1) { if(document.getElementById("user").value.search("@") !== -1) {
alert("User cannot contain '@' character!") alert("User cannot contain '@' character!")
return return;
} }
let r = await post('/register', { let r = (await post("/register",{
user: document.getElementById('user').value, user: document.getElementById("user").value,
pass: document.getElementById('pass').value, pass: document.getElementById("pass").value,
r: REDIRECT_URL, r: REDIRECT_URL
}) }))
if ( if(!r.url.endsWith("/user?success=true") && !r.url.endsWith(REDIRECT_URL)) {
!r.url.endsWith('/user?success=true') && if(r.url.endsWith("already_exists")) {
!r.url.endsWith(REDIRECT_URL) alert("An account with that name already exists! Did you mean to login?")
) {
if (r.url.endsWith('already_exists')) {
alert(
'An account with that name already exists! Did you mean to login?'
)
return return
} }
//fallback //fallback
document.getElementById('pass').value = '' document.getElementById("pass").value = ""
console.error('registration failed') console.error("registration failed")
alert('Registration failed') alert("Registration failed")
return return;
} }
window.location = REDIRECT_URL || '/user' window.location = REDIRECT_URL || "/user"
} }
function passkeydown(e) { function passkeydown(e) {
if (e.code === 'Enter') { if(e.code === "Enter") {
register() register()
} }
} }

View File

@ -1,11 +1,10 @@
const valuetoText = { const valuetoText = {
user: 'Username', "user":"Username",
post: 'Post', "post":"Post"
} }
function changed() { function changed() {
document.getElementById('selector').placeholder = document.getElementById("selector").placeholder = valuetoText[document.getElementById("type").value];
valuetoText[document.getElementById('type').value]
} }
async function getJSON(url) { async function getJSON(url) {
@ -13,83 +12,69 @@ async function getJSON(url) {
} }
async function submit() { async function submit() {
const type = document.getElementById('type').value const type = document.getElementById("type").value
const selector = document.getElementById('selector').value const selector = document.getElementById("selector").value
document.getElementById('output').innerHTML = '' document.getElementById("output").innerHTML=""
const res = await getJSON(`/api/search?type=${type}&selector=${selector}`) const res = await getJSON(`/api/search?type=${type}&selector=${selector}`)
//document.getElementById("output").innerHTML = res //document.getElementById("output").innerHTML = res
console.log(res) console.log(res);
for (let i = 0; i < res.length; i++) { for (let i = 0; i < res.length; i++) {
let obj = res[i] let obj = res[i]
if (type === 'user') { if(type === "user") {
createPost( createPost(decodeURIComponent(obj.User_Name || ""),decodeURIComponent(obj.User_Bio || "wow such empty"),0)
decodeURIComponent(obj.User_Name || ''), } else {
decodeURIComponent(obj.User_Bio || 'wow such empty'), createPost(decodeURIComponent(obj.post_user_name),decodeURIComponent(obj.post_text),obj.post_time,obj.post_special_text,obj.post_id)
0
)
} else {
createPost(
decodeURIComponent(obj.post_user_name),
decodeURIComponent(obj.post_text),
obj.post_time,
obj.post_special_text,
obj.post_id
)
}
} }
}
} }
function keydown(event) { function keydown(event) {
if (event.key === 'Enter') { if (event.key === "Enter") {
event.preventDefault() event.preventDefault()
submit() submit()
} }
} }
function spacerTextNode() { function spacerTextNode() {
return document.createTextNode(' | ') return document.createTextNode(" | ")
} }
function createPost(username, text, time, specialtext, postid) { function createPost(username,text,time,specialtext,postid) {
if (!specialtext) specialtext = '' if(!specialtext)specialtext=""
const newDiv = document.createElement('div') const newDiv = document.createElement("div");
const newP = document.createElement('p') const newP = document.createElement("p");
const newA = document.createElement('a') const newA = document.createElement("a");
const newSpan2 = document.createElement('span') const newSpan2 = document.createElement("span");
const newSpan3 = document.createElement('span') const newSpan3 = document.createElement("span");
const newUsername = document.createTextNode(username) const newUsername = document.createTextNode(username);
let timedate = new Date(time) let timedate = new Date(time)
time = timedate time = timedate
time = time.toString() time = time.toString()
time = time.split(' ') time = time.split(" ")
time = time = time[0] + " " + time[1] + " " + time[2] + " " + time[3] + " " + time[4]
time[0] + ' ' + time[1] + ' ' + time[2] + ' ' + time[3] + ' ' + time[4] if(timedate==="Thu Jan 01 1970 01:00:00 GMT+0100 (Central European Standard Time)")time=""
if ( const newTime = document.createTextNode(time)
timedate === const newSpecialText = document.createTextNode(specialtext)
'Thu Jan 01 1970 01:00:00 GMT+0100 (Central European Standard Time)'
)
time = ''
const newTime = document.createTextNode(time)
const newSpecialText = document.createTextNode(specialtext)
newDiv.classList.add('result') newDiv.classList.add("result");
newSpan3.classList.add('specialtext') newSpan3.classList.add("specialtext")
newA.appendChild(newUsername) newA.appendChild(newUsername)
newA.href = `/users/${username}` newA.href = `/users/${username}`
newSpan2.appendChild(newTime) newSpan2.appendChild(newTime)
newSpan3.appendChild(newSpecialText) newSpan3.appendChild(newSpecialText)
newP.appendChild(newA)
if (time !== '') newP.appendChild(spacerTextNode())
newP.appendChild(newSpan2)
if (specialtext !== '' && time !== '') newP.appendChild(spacerTextNode())
newP.appendChild(newSpan3)
newDiv.appendChild(newP) newP.appendChild(newA)
newDiv.innerHTML += filterPost(text) // skipqc if(time !== "")newP.appendChild(spacerTextNode())
newDiv.id = postid newP.appendChild(newSpan2)
document.getElementById('output').appendChild(newDiv) if(specialtext !== "" && time !== "")newP.appendChild(spacerTextNode())
newP.appendChild(newSpan3)
newDiv.appendChild(newP)
newDiv.innerHTML += filterPost(text) // skipqc
newDiv.id = postid
document.getElementById("output").appendChild(newDiv)
} }

View File

@ -1,158 +1,138 @@
function completeHandler(event) { function completeHandler(event) {
console.log('completed upload') console.log("completed upload");
console.log(event.target.responseText) console.log(event.target.responseText);
setuser() // skipqc setuser() // skipqc
} }
function errorHandler(event) { function errorHandler(event) {
console.log('error during upload') console.log("error during upload");
console.log(event.target.responseText) console.log(event.target.responseText);
} }
function progressHandler(event) { function progressHandler(event) {
console.log('progressing upload') console.log("progressing upload");
console.log('Uploaded ' + event.loaded + ' bytes of ' + event.total) console.log("Uploaded " + event.loaded + " bytes of " + event.total);
console.log(event.target.responseText) console.log(event.target.responseText);
} }
/** /**
* upload avatar to the server * upload avatar to the server
* @return {undefined} no return value * @return {undefined} no return value
*/ */
function uploadFile() { function uploadFile() {
const file = document.getElementById('avatarUpl').files[0] const file = document.getElementById("avatarUpl").files[0];
console.log(file) console.log(file);
const formdata = new FormData() const formdata = new FormData();
formdata.append('avatar', file) formdata.append("avatar", file);
const ajax = new XMLHttpRequest() const ajax = new XMLHttpRequest();
ajax.upload.addEventListener('progress', progressHandler, false) ajax.upload.addEventListener("progress", progressHandler, false);
ajax.addEventListener('load', completeHandler, false) ajax.addEventListener("load", completeHandler, false);
ajax.addEventListener('error', errorHandler, false) ajax.addEventListener("error", errorHandler, false);
ajax.addEventListener('abort', errorHandler, false) ajax.addEventListener("abort", errorHandler, false);
ajax.open('POST', '/api/setavatar') ajax.open("POST", "/api/setavatar");
ajax.send(formdata) ajax.send(formdata);
document.getElementById('avatarUplButton').style = 'display:none;' document.getElementById("avatarUplButton").style = "display:none;";
} }
function logout() { function logout() {
location.assign('/logout') location.assign('/logout')
} }
async function setuser() { async function setuser() {
let user = await (await fetch('/api/getuser')).json() let user = await (await fetch("/api/getuser")).json();
let username let username
let bio let bio
let avatar let avatar
username = user['username'] username = user["username"];
bio = user['bio'] bio = user["bio"]
avatar = user['avatar'] avatar = user["avatar"]
if (user['error']) username = user['error'] if(user["error"])username=user["error"];
if (user['error']) bio = user['error'] if(user["error"])bio=user["error"];
if (!bio) bio = 'wow such empty' if(!bio)bio="wow such empty"
if (avatar) { if(avatar) {
avatar = '/avatars/' + avatar avatar = "/avatars/"+avatar
} else { } else {
avatar = '/images/default_avatar.png' avatar = "/images/default_avatar.png"
} }
document.getElementById('user').innerText = `User: ${username}` document.getElementById("user").innerText = `User: ${username}`;
document.getElementById('bio').placeholder = decodeURIComponent(bio) document.getElementById("bio").placeholder = decodeURIComponent(bio);
document.getElementById('avatarimg').src = avatar document.getElementById("avatarimg").src = avatar;
document document.getElementById("avatarUpl").addEventListener("change", function(){
.getElementById('avatarUpl') document.getElementById("avatarUplButton").style = "";
.addEventListener('change', function () { })
document.getElementById('avatarUplButton').style = '' document.getElementById("avatarUplButton").addEventListener("click",uploadFile);
})
document
.getElementById('avatarUplButton')
.addEventListener('click', uploadFile)
} }
/** /**
* sets user bio * sets user bio
* @param {string} str - bio to set * @param {string} str - bio to set
* @return {promise} api response * @return {promise} api response
*/ */
function sendBio(str) { function sendBio(str) {
if (document.getElementById('bio').placeholder !== str && str !== '') { if(document.getElementById("bio").placeholder !== str && str !== "") {
document.getElementById('bio').placeholder = str document.getElementById("bio").placeholder = str
return post('/api/setBio', { Bio: str }) // skipqc return post("/api/setBio",{"Bio":str}) // skipqc
} }
return '' return ""
} }
async function bioChanger() { async function bioChanger() {
document.getElementById('bio').disabled = document.getElementById("bio").disabled = !document.getElementById("bio").disabled
!document.getElementById('bio').disabled document.getElementById("changeBio").innerText = (document.getElementById("bio").disabled && "Change Bio") || "Submit"
document.getElementById('changeBio').innerText = if(document.getElementById("bio").disabled) {
(document.getElementById('bio').disabled && 'Change Bio') || 'Submit' let response = await sendBio(document.getElementById("bio").value)
if (document.getElementById('bio').disabled) { console.log(response);
let response = await sendBio(document.getElementById('bio').value) document.getElementById("userstyle").innerHTML = '::placeholder {color: white;} #bio {border: 0px solid black; color:white;}'
console.log(response) }
document.getElementById('userstyle').innerHTML = else
'::placeholder {color: white;} #bio {border: 0px solid black; color:white;}' {
} else { document.getElementById("userstyle").innerHTML = '::placeholder {color: white;} #bio {border: 2px solid gray; color:white;}'
document.getElementById('userstyle').innerHTML = }
'::placeholder {color: white;} #bio {border: 2px solid gray; color:white;}'
}
} }
async function changePW() {
if (window.confirm('Are you sure that you want to change your Password?')) {
let re = await (
await post('/api/changePW', {
currentPW: document.getElementById('currentPW_pw').value,
newPW: document.getElementById('newPW').value,
})
).json() // skipqc
document.getElementById('response_pw').innerText =
re['error'] || re['success']
document.getElementById('response_pw').style = 'color:green'
if (re['error']) {
document.getElementById('response_pw').style = 'color:red'
}
document.getElementById('currentPW').value = ''
document.getElementById('newPW').value = ''
setuser() async function changePW() {
if(window.confirm("Are you sure that you want to change your Password?")){
let re = await (await post("/api/changePW",{"currentPW":document.getElementById("currentPW_pw").value,"newPW":document.getElementById("newPW").value})).json() // skipqc
document.getElementById("response_pw").innerText = re["error"] || re["success"]
document.getElementById("response_pw").style="color:green"
if(re["error"]) {
document.getElementById("response_pw").style="color:red"
} }
document.getElementById("currentPW").value = ""
document.getElementById("newPW").value = ""
setuser()
}
} }
async function changeUsername() { async function changeUsername() {
if (window.confirm('Are you sure that you want to change your Username?')) { if(window.confirm("Are you sure that you want to change your Username?")){
// skipqc // skipqc
let re = await ( let re = await (await post("/api/changeUsername",{"currentPW":document.getElementById("currentPW_us").value.toString(),"newUsername":document.getElementById("newUsername").value})).json()
await post('/api/changeUsername', { document.getElementById("response_us").innerText = re["error"] || re["success"]
currentPW: document document.getElementById("response_us").style="color:green"
.getElementById('currentPW_us') if(re["error"]) {
.value.toString(), document.getElementById("response_us").style="color:red"
newUsername: document.getElementById('newUsername').value,
})
).json()
document.getElementById('response_us').innerText =
re['error'] || re['success']
document.getElementById('response_us').style = 'color:green'
if (re['error']) {
document.getElementById('response_us').style = 'color:red'
}
document.getElementById('currentPW').value = ''
document.getElementById('newUsername').value = ''
setuser()
} }
document.getElementById("currentPW").value = ""
document.getElementById("newUsername").value = ""
setuser()
}
} }
async function setAllowCCR() { async function setAllowCCR() {
const ACCR = document.getElementById('ACCR_checkbox').checked const ACCR = document.getElementById("ACCR_checkbox").checked
const settingname = 'ACCR' //Allow Cross-Channel reply (see #22 ) const settingname = "ACCR" //Allow Cross-Channel reply (see #22 )
let r = await ( let r = await(await post("/api/settings",{setting: settingname, value: ACCR})).json() // skipqc
await post('/api/settings', { setting: settingname, value: ACCR })
).json() // skipqc
if (r.status === 'error') { if(r.status === "error") {
alert("Couldn't change setting") alert("Couldn't change setting")
console.log(r.code) console.log(r.code)
} else if (r.status === 'success') { } else if(r.status === "success") {
//changed setting //changed setting
} }
} }

View File

@ -1,54 +1,53 @@
function getCookie(cname) { function getCookie(cname) {
let name = cname + '=' let name = cname + "=";
let decodedCookie = decodeURIComponent(document.cookie) let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(';') let ca = decodedCookie.split(';');
for (let i = 0; i < ca.length; i++) { for(let i = 0; i <ca.length; i++) {
let c = ca[i] let c = ca[i];
while (c.charAt(0) === ' ') { while (c.charAt(0) ===' ') {
c = c.substring(1) c = c.substring(1);
}
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length)
}
} }
return '' if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
}
}
return "";
} }
function setCookie(cname, cvalue, exdays) { function setCookie(cname, cvalue, exdays) {
const d = new Date() const d = new Date();
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000) d.setTime(d.getTime() + (exdays*24*60*60*1000));
let expires = 'expires=' + d.toUTCString() let expires = "expires="+ d.toUTCString();
document.cookie = cname + '=' + cvalue + ';' + expires + ';path=/' document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
} }
function logout() { function logout() {
localStorage.setItem('priv_key', '') localStorage.setItem("priv_key","")
localStorage.setItem('decryption_key', '') localStorage.setItem("decryption_key","")
location.assign('/logout') location.assign('/logout')
} }
async function setuser() { async function setuser() {
if (getCookie('priv_key') !== '') { if(getCookie("priv_key") !== "") {
localStorage.setItem('priv_key', getCookie('priv_key')) localStorage.setItem("priv_key",getCookie("priv_key"))
setCookie('priv_key', '', 0) setCookie("priv_key","",0)
} }
let user = await (await fetch('/api/getuser')).json() let user = await (await fetch("/api/getuser")).json();
let username let username
let bio let bio
let avatar let avatar
username = user['username'] username = user["username"];
bio = user['bio'] bio = user["bio"]
avatar = user['avatar'] avatar = user["avatar"]
if (user['error']) username = user['error'] if(user["error"])username=user["error"];
if (user['error']) bio = user['error'] if(user["error"])bio=user["error"];
if (!bio) bio = 'wow such empty' if(!bio)bio="wow such empty"
if (avatar) { if(avatar) {
avatar = '/avatars/' + avatar avatar = "/avatars/"+avatar
} else { } else {
avatar = '/images/default_avatar.png' avatar = "/images/default_avatar.png"
} }
document.getElementById('user').innerText = `User: ${username}` document.getElementById("user").innerText = `User: ${username}`;
document.getElementById('userBio').innerText = document.getElementById("userBio").innerText = "Bio: " + decodeURIComponent(bio);
'Bio: ' + decodeURIComponent(bio) document.getElementById("avatarimg").src = avatar;
document.getElementById('avatarimg').src = avatar
} }

View File

@ -1,13 +1,10 @@
const warn_messages = [ const warn_messages = [
[ ["%cDo not paste any text in here","background: red; color: yellow; font-size: x-large"],
'%cDo not paste any text in here', ["Pasting anything in here may give others access to your account.",""]
'background: red; color: yellow; font-size: x-large',
],
['Pasting anything in here may give others access to your account.'],
] ]
function warnmessage() { function warnmessage() {
for (let message of warn_messages) { for (let message of warn_messages) {
console.log(message[0], message[1]) console.log(message[0],message[1]);
} }
} }
const warn_message_int = setInterval(warnmessage, 3000) const warn_message_int = setInterval(warnmessage,3000)

9378
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +1,47 @@
{ {
"dependencies": { "dependencies": {
"body-parser": "^2.2", "body-parser": "^1.20.2",
"clean-css": "^5.3", "clean-css": "^5.3.2",
"compression": "^1.8", "compression": "^1.7.4",
"cookie-parser": "^1.4", "cookie-parser": "^1.4.6",
"cookie-signature": "^1.2.2", "ejs": "^3.1.9",
"ejs": "^3.1", "express": "^4.18.2",
"express": "^5.1", "express-fileupload": "^1.3.1",
"express-fileupload": "^1.5", "express-useragent": "^1.0.15",
"express-useragent": "^1.0", "hcaptcha": "^0.1.1",
"hcaptcha": "^0.2", "hsts": "^2.2.0",
"html-minifier-terser": "^7.2.0", "newrelic": "^9.15.0",
"lru-cache": "^11.1", "html-minifier-terser": "^7.2.0",
"mysql2": "^3.14", "lru-cache": "^10.0.0",
"sharp": "^0.34", "mysql2": "^3.3.5",
"swagger-autogen": "^2.23", "newrelic": "^9.11.0",
"uglify-js": "^3.19", "sharp": "^0.30.7",
"unsafe_encrypt": "^1.0.4", "spdy": "^4.0.2",
"ws": "^8.18" "swagger-autogen": "^2.23.1",
}, "uglify-js": "^3.17.4",
"scripts": { "unsafe_encrypt": "^1.0.4",
"start": "node server.js", "ws": "^8.13.0"
"test": "node tests", },
"prepare": "husky install" "scripts": {
}, "start": "node server.js",
"type": "module", "test": "node tests"
"name": "ipost", },
"description": "IPost is a revolutionary open-source chatting platform featuring an innovative design", "type": "module",
"version": "1.0.0", "name": "ipost",
"main": "server.js", "description": "IPost is a revolutionary open-source chatting platform featuring an innovative design",
"repository": { "version": "1.0.0",
"type": "git", "main": "server.js",
"url": "git+https://github.com/002Hub/IPost.git" "repository": {
}, "type": "git",
"author": "", "url": "git+https://github.com/002Hub/IPost.git"
"license": "ISC", },
"bugs": { "author": "",
"url": "https://github.com/002Hub/IPost/issues" "license": "ISC",
}, "bugs": {
"homepage": "https://github.com/002Hub/IPost#readme", "url": "https://github.com/002Hub/IPost/issues"
"devDependencies": { },
"@hcaptcha/types": "^1.0.3", "homepage": "https://github.com/002Hub/IPost#readme",
"husky": "^9.1.7", "devDependencies": {
"lint-staged": "^15.5.1", "@hcaptcha/types": "^1.0.3"
"prettier": "^3.5" }
},
"lint-staged": {
"*.{js,css,md}": "prettier --write"
}
} }

1987
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
onlyBuiltDependencies:
- sharp

View File

@ -1,6 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
]
}

View File

@ -1,148 +1,119 @@
import fs from 'fs' import fs from "fs";
import { SHA256 } from '../../extra_modules/SHA.js' import {SHA256} from "../../extra_modules/SHA.js";
import { unsign } from '../../extra_modules/unsign.js' import {unsign} from "../../extra_modules/unsign.js";
const config = JSON.parse(fs.readFileSync('server_config.json')) const config = JSON.parse(fs.readFileSync("server_config.json"));
const HASHES_DB = config.cookies.server_hashes const HASHES_DB = config.cookies.server_hashes;
const HASHES_COOKIE = config.cookies.client_hashes const HASHES_COOKIE = config.cookies.client_hashes;
const HASHES_DIFF = HASHES_DB - HASHES_COOKIE const HASHES_DIFF = HASHES_DB - HASHES_COOKIE;
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
router.use('/*path', (req, res, next) => { router.use("/*", (req, res, next) => {
res.set('Access-Control-Allow-Origin', '*') //we'll allow it for now res.set("Access-Control-Allow-Origin", "*"); //we'll allow it for now
let unsigned let unsigned;
if(typeof req.get("ipost-auth-token") === "string") {
req.body = req.body || {} try{
req.body.auth = JSON.parse(req.get("ipost-auth-token"))
if (typeof req.get('ipost-auth-token') === 'string') { } catch(err) {
try { console.log("error parsing header",err)
req.body.auth = JSON.parse(req.get('ipost-auth-token'))
} catch (err) {
console.log('error parsing header', err)
} }
} }
if ( if(req.body.auth !== undefined && req.originalUrl !== "/redeemauthcode") {
req.body.auth !== undefined && if(typeof req.body.auth === "string") {
req.originalUrl !== '/redeemauthcode' try{
) {
if (typeof req.body.auth === 'string') {
try {
req.body.auth = JSON.parse(req.body.auth) req.body.auth = JSON.parse(req.body.auth)
} catch (err) { } catch(err) {
console.log('error parsing', err) console.log("error parsing",err)
} }
} else if ( } else
typeof req.body.auth !== 'object' || if(
typeof req.body.auth.secret !== 'string' || typeof req.body.auth !== "object" ||
typeof req.body.auth.appid !== 'number' || typeof req.body.auth.secret !== "string" ||
typeof req.body.auth.auth_token !== 'string' || typeof req.body.auth.appid !== "number" ||
req.body.auth.secret.length !== 200 || typeof req.body.auth.auth_token !== "string" ||
req.body.auth.auth_token.length !== 200 || req.body.auth.secret.length !== 200 ||
Buffer.from(req.body.auth.secret, 'base64').length !== 150 req.body.auth.auth_token.length !== 200 ||
Buffer.from(req.body.auth.secret,"base64").length !== 150
) { ) {
res.status(420).send('invalid authentication object') res.status(420).send("invalid authentication object")
return return;
} else { } else {
//secret : string(200 chars) //secret : string(200 chars)
//appid : number //appid : number
//auth_token: string(200 chars) //auth_token: string(200 chars)
let sql = let sql = "select User_ID,User_Name,User_Bio,User_Avatar,User_Settings from ipost.auth_tokens inner join ipost.application on auth_token_isfrom_application_id=application_id inner join ipost.users on auth_token_u_id=User_ID where auth_token=? and application_secret=? and application_id=?"
'select User_ID,User_Name,User_Bio,User_Avatar,User_Settings from ipost.auth_tokens inner join ipost.application on auth_token_isfrom_application_id=application_id inner join ipost.users on auth_token_u_id=User_ID where auth_token=? and application_secret=? and application_id=?' con.query(sql,[SHA256(req.body.auth.auth_token,req.body.auth.appid, HASHES_DB),SHA256(req.body.auth.secret,req.body.auth.appid, HASHES_DB),req.body.auth.appid],(err,result) => {
con.query( if(err) throw err;
sql,
[
SHA256(
req.body.auth.auth_token,
req.body.auth.appid,
HASHES_DB
),
SHA256(
req.body.auth.secret,
req.body.auth.appid,
HASHES_DB
),
req.body.auth.appid,
],
(err, result) => {
if (err) throw err
if (result.length !== 1) { if(result.length !== 1) {
res.status(420).send( res.status(420).send("invalid authentication object (or server error?)")
'invalid authentication object (or server error?)' return;
)
return
}
res.locals.userid = result[0].User_ID
res.locals.username = result[0].User_Name
res.locals.bio = result[0].User_Bio || ''
res.locals.avatar = result[0].User_Avatar || ''
res.locals.settings = result[0].User_Settings || {}
res.locals.isbot = true //only apps/bots use auth tokens
next()
} }
)
return res.locals.userid = result[0].User_ID;
res.locals.username = result[0].User_Name;
res.locals.bio = result[0].User_Bio || "";
res.locals.avatar = result[0].User_Avatar || "";
res.locals.settings = result[0].User_Settings || {};
res.locals.isbot = true; //only apps/bots use auth tokens
next()
})
return;
} }
} else { } else {
if (!req.cookies.AUTH_COOKIE) { if(!req.cookies.AUTH_COOKIE) {
next() next()
return return
} }
unsigned = unsign(req.cookies.AUTH_COOKIE, req, res) unsigned = unsign(req.cookies.AUTH_COOKIE, req, res);
if (!unsigned) { if (!unsigned){
next() next()
return return
} }
} }
let sql = `select User_ID,User_Name,User_Bio,User_Avatar,User_Settings from ipost.users where User_Name=? and User_PW=?;` let sql = `select User_ID,User_Name,User_Bio,User_Avatar,User_Settings from ipost.users where User_Name=? and User_PW=?;`;
let values = unsigned.split(' ') let values = unsigned.split(" ");
values[1] = SHA256(values[1], values[0], HASHES_DIFF) values[1] = SHA256(values[1], values[0], HASHES_DIFF);
res.locals.bio = '' res.locals.bio = "";
res.locals.avatar = '' res.locals.avatar = "";
res.locals.settings = {} res.locals.settings = {};
con.query(sql, values, function (err, result) { con.query(sql, values, function (err, result) {
if (err) throw err if (err)
if ( throw err;
result[0] && if (result[0] && result[0].User_Name && result[0].User_Name === values[0]) {
result[0].User_Name &&
result[0].User_Name === values[0] res.locals.userid = result[0].User_ID;
) { res.locals.username = result[0].User_Name;
res.locals.userid = result[0].User_ID res.locals.bio = result[0].User_Bio || "";
res.locals.username = result[0].User_Name res.locals.avatar = result[0].User_Avatar || "";
res.locals.bio = result[0].User_Bio || '' res.locals.settings = result[0].User_Settings || {};
res.locals.avatar = result[0].User_Avatar || ''
res.locals.settings = result[0].User_Settings || {}
} }
next() next()
}) });
}) });
router.use('/api/*path', (req, res, next) => { router.use("/api/*", (req, res, next) => {
res.set('Access-Control-Allow-Origin', '*') //we'll allow it for now res.set("Access-Control-Allow-Origin", "*"); //we'll allow it for now
if ( if (config["allow_getotheruser_without_cookie"] && req.originalUrl.split("\?")[0] === "/api/getotheruser") {
config['allow_getotheruser_without_cookie'] && next();
req.originalUrl.split('\?')[0] === '/api/getotheruser' return;
) {
next()
return
} }
if (!server.increaseAPICall(req, res)) return if (!server.increaseAPICall(req, res))return;
if (res.locals.username !== undefined) { if (res.locals.username !== undefined) {
next() next();
} else { }
res.status(402) else {
res.json({ res.status(402);
error: 'you cannot access the api without being logged in', res.json({ "error": "you cannot access the api without being logged in" });
})
} }
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
} };
export default { export default {
setup, setup
} };

View File

@ -1,94 +1,71 @@
//const web_version = require("unsafe_encrypt").web_version //const web_version = require("unsafe_encrypt").web_version
import { web_version } from 'unsafe_encrypt' import { web_version } from "unsafe_encrypt";
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
router.get('/api/getPersonalPosts', function (req, res) { router.get("/api/getPersonalPosts", function (req, res) {
res.set('Access-Control-Allow-Origin', '') res.set("Access-Control-Allow-Origin", "");
let otherperson = encodeURIComponent(req.query.otherperson || '') let otherperson = encodeURIComponent(req.query.otherperson || "");
if ( if (typeof otherperson !== "string" || otherperson.length > 100 || otherperson === "") {
typeof otherperson !== 'string' || res.status(410).json({ "error": "invalid otherperson given" });
otherperson.length > 100 || return;
otherperson === ''
) {
res.status(410).json({ error: 'invalid otherperson given' })
return
} }
const columns = [ const columns = [
'dms_user_name', "dms_user_name", "dms_text", "dms_time", "dms_special_text", "dms_id", "dms_from_bot", "dms_reply_id"
'dms_text', ];
'dms_time',
'dms_special_text',
'dms_id',
'dms_from_bot',
'dms_reply_id',
]
//dms_user_name = sender //dms_user_name = sender
//dms_receiver = receiver //dms_receiver = receiver
//if (sender == current and receiver == other) or (receiver == current and sender == other) //if (sender == current and receiver == other) or (receiver == current and sender == other)
let sql = `select ${columns.join(',')} from ipost.dms where ((dms_receiver = ? and dms_user_name = ?) or (dms_receiver = ? and dms_user_name = ?)) order by dms_id desc limit 50;` let sql = `select ${columns.join(",")} from ipost.dms where ((dms_receiver = ? and dms_user_name = ?) or (dms_receiver = ? and dms_user_name = ?)) order by dms_id desc limit 50;`;
con.query( con.query(sql, [otherperson, encodeURIComponent(res.locals.username), encodeURIComponent(res.locals.username), otherperson], function (err, result) {
sql, if (err)
[ throw err;
otherperson, res.json(result);
encodeURIComponent(res.locals.username), });
encodeURIComponent(res.locals.username),
otherperson,
],
function (err, result) {
if (err) throw err
res.json(result)
}
)
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
router.get('/api/dms/conversations', function (req, res) { router.get("/api/dms/conversations", function (req, res) {
res.set('Access-Control-Allow-Origin', '*') res.set("Access-Control-Allow-Origin", "*");
let uriencusername = encodeURIComponent(res.locals.username) let uriencusername = encodeURIComponent(res.locals.username);
let sql = `select dms_user_name, dms_receiver from ipost.dms where ((dms_receiver = ?) or (dms_user_name = ?)) group by dms_receiver,dms_user_name;` let sql = `select dms_user_name, dms_receiver from ipost.dms where ((dms_receiver = ?) or (dms_user_name = ?)) group by dms_receiver,dms_user_name;`;
con.query( con.query(sql, [uriencusername, uriencusername], function (err, result) {
sql, if (err)
[uriencusername, uriencusername], throw err;
function (err, result) { res.json(result);
if (err) throw err });
res.json(result)
}
)
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
router.get('/api/dms/encrypt.js', function (req, res) { router.get("/api/dms/encrypt.js", function (req, res) {
res.set('Access-Control-Allow-Origin', '*') res.set("Access-Control-Allow-Origin", "*");
res.send(web_version()) res.send(web_version());
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
// //
router.get('/api/dms/getDM', function (req, res) { router.get("/api/dms/getDM", function (req, res) {
res.set('Access-Control-Allow-Origin', '*') res.set("Access-Control-Allow-Origin", "*");
let arg = req.query.id let arg = req.query.id;
let uriencusername = encodeURIComponent(res.locals.username) let uriencusername = encodeURIComponent(res.locals.username);
let sql = `select dms_user_name,dms_text,dms_time,dms_special_text,dms_id,dms_from_bot,dms_reply_id,dms_receiver from ipost.dms where dms_id=? and (dms_user_name=? or dms_receiver=?);` let sql = `select dms_user_name,dms_text,dms_time,dms_special_text,dms_id,dms_from_bot,dms_reply_id,dms_receiver from ipost.dms where dms_id=? and (dms_user_name=? or dms_receiver=?);`;
con.query( con.query(sql, [arg, uriencusername, uriencusername], function (err, result) {
sql, if (err)
[arg, uriencusername, uriencusername], throw err;
function (err, result) { if (result[0]) {
if (err) throw err res.set('Cache-Control', 'public, max-age=2592000'); //cache it for one month-ish
if (result[0]) { res.json(result[0]);
res.set('Cache-Control', 'public, max-age=2592000') //cache it for one month-ish
res.json(result[0])
} else {
res.json({ error: 'there is no such dm!' })
}
} }
) else {
res.json({ "error": "there is no such dm!" });
}
});
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
} };
export default { export default {
setup, setup
} };

View File

@ -1,116 +1,104 @@
import xor from '../../../extra_modules/xor.js' import xor from "../../../extra_modules/xor.js";
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
const PIDS = {} //[pid]: true/"already_used" const PIDS = {}; //[pid]: true/"already_used"
function createPID() { function createPID(){
let pid = server.genstring(10) //collision chance is low enough, but we'll check anyways let pid = server.genstring(10); //collision chance is low enough, but we'll check anyways
while (PIDS[pid] !== undefined) { while (PIDS[pid] !== undefined) {
pid = server.genstring(10) pid = server.genstring(10);
console.log(5, 'pid collision') console.log(5, "pid collision");
} }
PIDS[pid] = true PIDS[pid] = true;
setTimeout(function () { setTimeout(function() {
PIDS[pid] = undefined PIDS[pid] = undefined;
}, 40000) }, 40000);
return pid return pid
} }
router.get('/api/dms/pid', function (req, res) {
res.set('Access-Control-Allow-Origin', '*') router.get("/api/dms/pid", function (req, res) {
res.json({ pid: createPID() }) res.set("Access-Control-Allow-Origin", "*");
res.json({ "pid": createPID() });
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
router.post('/api/dms/post', function (req, res) { router.post("/api/dms/post", function (req, res) {
if (!req.body.message) { if (!req.body.message) {
res.status(410) res.status(410)
res.json({ error: 'no message to post' }) res.json({ "error": "no message to post" });
return return;
} }
if (typeof req.body.message !== 'string') { if ((typeof req.body.message) !== "string") {
res.status(411) res.status(411)
res.json({ error: 'no message to post' }) res.json({ "error": "no message to post" });
return return;
} }
if (typeof req.body.pid !== 'string') { if ((typeof req.body.pid) !== "string") {
res.status(412) res.status(412)
res.json({ error: 'no pid given' }) res.json({ "error": "no pid given" });
return return;
} }
if (req.body.pid.length !== 10 || PIDS[req.body.pid] !== true) { if (req.body.pid.length !== 10 || PIDS[req.body.pid] !== true) {
res.status(413) res.status(413)
res.json({ error: 'invalid pid given' }) res.json({ "error": "invalid pid given" });
return return;
} }
PIDS[req.body.pid] = 'already_used' PIDS[req.body.pid] = "already_used";
let reply_id let reply_id;
if (!req.body.reply_id || req.body.reply_id < 0) { if (!req.body.reply_id || req.body.reply_id < 0) {
reply_id = 0 reply_id = 0;
} else {
reply_id = req.body.reply_id
} }
if (typeof reply_id !== 'number') { else {
reply_id = req.body.reply_id;
}
if ((typeof reply_id) !== "number") {
res.status(414) res.status(414)
res.json({ error: 'no valid reply id given' }) res.json({ "error": "no valid reply id given" });
return return;
} }
if (req.body.message.length > 1000) { if (req.body.message.length > 1000) {
res.status(415) res.status(415)
res.json({ error: 'message too long' }) res.json({ "error": "message too long" });
return return;
} }
req.body.message = encodeURIComponent(req.body.message.trim()) req.body.message = encodeURIComponent(req.body.message.trim());
if (req.body.message.length > 3000) { if (req.body.message.length > 3000) {
res.status(416) res.status(416)
res.json({ error: 'message too long' }) //check again after URI encoding it res.json({ "error": "message too long" }); //check again after URI encoding it
return return;
} }
req.body.receiver = encodeURIComponent(req.body.receiver || '') req.body.receiver = encodeURIComponent(req.body.receiver || "");
if ( if (req.body.receiver === "" || req.body.receiver === encodeURIComponent(res.locals.username) || req.body.receiver.length > 100) {
req.body.receiver === '' || res.status(417).json({ "error": "invalid receiver given" });
req.body.receiver === encodeURIComponent(res.locals.username) || return;
req.body.receiver.length > 100
) {
res.status(417).json({ error: 'invalid receiver given' })
return
} }
let otherperson = req.body.receiver let otherperson = req.body.receiver;
if (!req.body.message) { if (!req.body.message) {
res.status(418) res.status(418)
res.json({ error: 'no message to post' }) res.json({ "error": "no message to post" });
return return;
} }
let sql = `insert into ipost.dms (dms_user_name,dms_text,dms_time,dms_receiver,dms_from_bot,dms_reply_id) values (?,?,?,?,?,?);` let sql = `insert into ipost.dms (dms_user_name,dms_text,dms_time,dms_receiver,dms_from_bot,dms_reply_id) values (?,?,?,?,?,?);`;
let values = [ let values = [encodeURIComponent(res.locals.username), req.body.message, Date.now(), otherperson, res.locals.isbot, reply_id];
encodeURIComponent(res.locals.username),
req.body.message,
Date.now(),
otherperson,
res.locals.isbot,
reply_id,
]
con.query(sql, values, function (err, result) { con.query(sql, values, function (err, result) {
if (err) { if (err) {
res.status(500) res.status(500)
res.json({ error: "there's been an internal error" }) res.json({"error":"there's been an internal error"})
console.error(err) console.error(err)
return return;
} }
res.json({ success: 'successfully posted dm' }) res.json({ "success": "successfully posted dm" });
console.log( console.log(5, `posted new dm by ${res.locals.username} to ${otherperson} : ${xor(encodeURIComponent(res.locals.username), otherperson)}`);
5, });
`posted new dm by ${res.locals.username} to ${otherperson} : ${xor(encodeURIComponent(res.locals.username), otherperson)}`
)
})
//TODO: bring dms up-to-date with normal posts //TODO: bring dms up-to-date with normal posts
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
return createPID return createPID
} };
export default { export default {
setup, setup
} };

View File

@ -1,12 +1,12 @@
import sharp from 'sharp' import sharp from "sharp"
async function addTextOnImage(text, buf) { async function addTextOnImage(text,buf) {
try { try {
let img = await sharp(buf) let img = await sharp(buf)
const metadata = await img.metadata() const metadata = await img.metadata()
const width = metadata.width const width = metadata.width;
const height = metadata.height const height = metadata.height;
const svgImage = ` const svgImage = `
<svg width="${width}" height="${height}"> <svg width="${width}" height="${height}">
@ -15,35 +15,30 @@ async function addTextOnImage(text, buf) {
</style> </style>
<text x="50%" y="50%" text-anchor="middle" class="title">${text}</text> <text x="50%" y="50%" text-anchor="middle" class="title">${text}</text>
</svg> </svg>
` `;
return await img return await img
.composite([ .composite([
{ {
input: Buffer.from(svgImage), input: Buffer.from(svgImage),
top: 0, top: 0,
left: 0, left: 0,
}, },
]) ]).webp({effort:6}).toBuffer()
.webp({ effort: 6 })
.toBuffer()
} catch (error) { } catch (error) {
console.log(error) console.log(error);
} }
} }
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
router.get('/api/getFileIcon/:icon', async function (req, res) { router.get("/api/getFileIcon/:icon",async function(req,res){
let path = req.params.icon let path = req.params.icon
if (path.length > 4) { if(path.length > 4) {
res.status(410).json({ error: 'file ending is too long' }) res.status(410).json({"error":"file ending is too long"})
return return;
} }
addTextOnImage( addTextOnImage(path,await sharp("./images/empty_file.png").toBuffer()).then(buf => {
path, res.set("content-type","image/png")
await sharp('./images/empty_file.png').toBuffer()
).then((buf) => {
res.set('content-type', 'image/png')
res.send(buf) res.send(buf)
}) })
/* #swagger.security = [{ /* #swagger.security = [{

View File

@ -1,67 +1,65 @@
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
router.get('/api/getPosts', function (req, res) { router.get("/api/getPosts", function (req, res) {
res.set('Access-Control-Allow-Origin', '*') res.set("Access-Control-Allow-Origin", "*");
if (req.query.channel !== undefined) { if (req.query.channel !== undefined) {
let sql = `select post_user_name,post_text,post_time,post_special_text,post_id,post_from_bot,post_reply_id,User_Avatar,file_0,file_1,file_2,file_3,file_4 from ipost.posts inner join ipost.users on (User_Name = post_user_name) where post_receiver_name = ? group by post_id order by post_id desc limit 30;` let sql = `select post_user_name,post_text,post_time,post_special_text,post_id,post_from_bot,post_reply_id,User_Avatar,file_0,file_1,file_2,file_3,file_4 from ipost.posts inner join ipost.users on (User_Name = post_user_name) where post_receiver_name = ? group by post_id order by post_id desc limit 30;`;
con.query( con.query(sql, [encodeURIComponent(req.query.channel)], function (err, result) {
sql, if (err)
[encodeURIComponent(req.query.channel)], throw err;
function (err, result) { res.json(result);
if (err) throw err });
res.json(result) }
} else { //fallback
) let sql = `select post_user_name,post_text,post_time,post_special_text,post_id,post_from_bot,post_reply_id,file_0,file_1,file_2,file_3,file_4 from ipost.posts where (post_receiver_name is null or post_receiver_name = 'everyone') group by post_id order by post_id desc limit 30;`;
} else {
//fallback
let sql = `select post_user_name,post_text,post_time,post_special_text,post_id,post_from_bot,post_reply_id,file_0,file_1,file_2,file_3,file_4 from ipost.posts where (post_receiver_name is null or post_receiver_name = 'everyone') group by post_id order by post_id desc limit 30;`
con.query(sql, [], function (err, result) { con.query(sql, [], function (err, result) {
if (err) throw err if (err)
res.json(result) throw err;
}) res.json(result);
});
} }
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
router.get('/api/getPostsLowerThan', function (req, res) { router.get("/api/getPostsLowerThan", function (req, res) {
res.set('Access-Control-Allow-Origin', '*') res.set("Access-Control-Allow-Origin", "*");
if (req.query.channel !== undefined) { if (req.query.channel !== undefined) {
let sql = `select post_user_name,post_text,post_time,post_special_text,post_id,post_from_bot,post_reply_id,file_0,file_1,file_2,file_3,file_4 from ipost.posts where ((post_receiver_name = ?) and (post_id < ?)) group by post_id order by post_id desc limit 30;` let sql = `select post_user_name,post_text,post_time,post_special_text,post_id,post_from_bot,post_reply_id,file_0,file_1,file_2,file_3,file_4 from ipost.posts where ((post_receiver_name = ?) and (post_id < ?)) group by post_id order by post_id desc limit 30;`;
con.query( con.query(sql, [encodeURIComponent(req.query.channel), req.query.id], function (err, result) {
sql, if (err)
[encodeURIComponent(req.query.channel), req.query.id], throw err;
function (err, result) { res.json(result);
if (err) throw err });
res.json(result) }
} else { //fallback
) let sql = `select post_user_name,post_text,post_time,post_special_text,post_id,post_from_bot,post_reply_id,file_0,file_1,file_2,file_3,file_4 from ipost.posts where ((post_receiver_name is null or post_receiver_name = 'everyone') and (post_id < ?)) group by post_id order by post_id desc limit 30;`;
} else {
//fallback
let sql = `select post_user_name,post_text,post_time,post_special_text,post_id,post_from_bot,post_reply_id,file_0,file_1,file_2,file_3,file_4 from ipost.posts where ((post_receiver_name is null or post_receiver_name = 'everyone') and (post_id < ?)) group by post_id order by post_id desc limit 30;`
con.query(sql, [req.query.id], function (err, result) { con.query(sql, [req.query.id], function (err, result) {
if (err) throw err if (err)
res.json(result) throw err;
}) res.json(result);
});
} }
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
router.get('/api/getPost', function (req, res) { router.get("/api/getPost", function (req, res) {
res.set('Access-Control-Allow-Origin', '*') res.set("Access-Control-Allow-Origin", "*");
let arg = req.query.id let arg = req.query.id;
let sql = `select post_user_name,post_text,post_time,post_special_text,post_id,post_from_bot,post_reply_id,post_receiver_name,User_Avatar,file_0,file_1,file_2,file_3,file_4 from ipost.posts inner join ipost.users on (User_Name = post_user_name) where post_id=?;` let sql = `select post_user_name,post_text,post_time,post_special_text,post_id,post_from_bot,post_reply_id,post_receiver_name,User_Avatar,file_0,file_1,file_2,file_3,file_4 from ipost.posts inner join ipost.users on (User_Name = post_user_name) where post_id=?;`;
con.query(sql, [arg], function (err, result) { con.query(sql, [arg], function (err, result) {
if (err) throw err if (err)
throw err;
if (result[0]) { if (result[0]) {
res.set('Cache-Control', 'public, max-age=2592000') //cache it for one month-ish res.set('Cache-Control', 'public, max-age=2592000'); //cache it for one month-ish
res.json(result[0]) res.json(result[0]);
} else {
res.json({ error: 'there is no such post!' })
} }
}) else {
res.json({ "error": "there is no such post!" });
}
});
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
} }

View File

@ -1,18 +1,18 @@
function allowAllTraffic(router, str, type) { function allowAllTraffic(router, str, type) {
router.options(str, function (req, res, next) { router.options(str, function (req, res, next) {
res.set('Access-Control-Allow-Origin', 'test.ipost.rocks') //we'll allow it for now res.set("Access-Control-Allow-Origin", "*"); //we'll allow it for now
res.set('Access-Control-Allow-Methods', type || 'GET') res.set("Access-Control-Allow-Methods", type || "GET");
res.set('Access-Control-Allow-Headers', 'Content-Type') res.set("Access-Control-Allow-Headers", "Content-Type");
res.status(200).send('') res.status(200).send("");
}) });
} }
function setup(router, con, server) { function setup(router, con, server) {
allowAllTraffic(router, '/api/pid') allowAllTraffic(router, "/api/pid");
allowAllTraffic(router, '/api/post', 'POST') allowAllTraffic(router, "/api/post", "POST");
allowAllTraffic(router, '/api/getotheruser') allowAllTraffic(router, "/api/getotheruser");
allowAllTraffic(router, '/api/getPost') allowAllTraffic(router, "/api/getPost");
allowAllTraffic(router, '/api/getPostsLowerThan') allowAllTraffic(router, "/api/getPostsLowerThan");
allowAllTraffic(router, '/api/settings') allowAllTraffic(router, "/api/settings");
allowAllTraffic(router, '/api/settings', 'POST') allowAllTraffic(router, "/api/settings", "POST");
} }
export { setup } export { setup };

View File

@ -1,261 +1,218 @@
import sharp from 'sharp' import sharp from "sharp";
import { writeFile } from 'fs' import {writeFile} from "fs";
const image_types = { const image_types = {
png: true, "png":true,
jpg: true, "jpg":true,
jpeg: true, "jpeg":true,
webp: true, "webp":true,
jfif: true, "jfif":true
} }
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
const PIDS = {} //[pid]: true/"already_used" const PIDS = {}; //[pid]: true/"already_used"
function isNotNull(a) { function isNotNull(a) {
return typeof a !== 'undefined' && a !== null return typeof a !== "undefined" && a !== null
} }
function createPID() { function createPID(){
let pid = server.genstring(10) //collision chance is low enough, but we'll check anyways let pid = server.genstring(10); //collision chance is low enough, but we'll check anyways
while (PIDS[pid] !== undefined) { while (PIDS[pid] !== undefined) {
pid = server.genstring(10) pid = server.genstring(10);
console.log(5, 'pid collision') console.log(5, "pid collision");
} }
PIDS[pid] = true PIDS[pid] = true;
setTimeout(function () { setTimeout(function() {
PIDS[pid] = undefined PIDS[pid] = undefined;
}, 40000) }, 40000);
return pid return pid
} }
router.get('/api/pid', function (req, res) { router.get("/api/pid", function (req, res) {
res.set('Access-Control-Allow-Origin', '*') res.set("Access-Control-Allow-Origin", "*");
res.json({ pid: createPID() }) res.json({ "pid": createPID() });
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
function validateMessage(message) { function validateMessage(message) {
if (!message) { if (!message) {
throw { throw {
statusCode: 410, statusCode: 410,
message: 'no message to post', message: "no message to post"
} }
} }
if (typeof message !== 'string') { if ((typeof message) !== "string") {
throw { throw {
statusCode: 411, statusCode: 411,
message: 'no message to post', message: "no message to post"
} }
} }
if (message.length > 1000) { if (message.length > 1000) {
throw { throw {
statusCode: 416, statusCode: 416,
message: 'message too long', message: "message too long"
} }
} }
message = encodeURIComponent(message.trim()) message = encodeURIComponent(message.trim());
if (message.length > 3000) { if (message.length > 3000) {
throw { throw {
statusCode: 417, statusCode: 417,
message: 'message too long', message: "message too long"
} }
} }
if (!message) { if (!message) {
throw { throw {
statusCode: 418, statusCode: 418,
message: 'no message to post', message: "no message to post"
} }
} //backup check } //backup check
return message return message
} }
function validatePID(pid) { function validatePID(pid) {
if (!pid || typeof pid !== 'string') { if (!pid || typeof pid !== "string") {
throw { throw {
statusCode: 412, statusCode: 412,
message: 'no pid given', message: "no pid given"
} }
} }
if (pid.length !== 10 || PIDS[pid] !== true) { if (pid.length !== 10 || PIDS[pid]!==true) {
throw { throw {
statusCode: 413, statusCode: 413,
message: 'invalid pid given', message: "invalid pid given"
} }
} }
PIDS[pid] = 'already_used' PIDS[pid] = "already_used";
} }
function validateReplyID(rid) { function validateReplyID(rid) {
let reply_id let reply_id;
if (!rid || rid < 0) { if (!rid || rid < 0) {
reply_id = 0 reply_id = 0
} }
if (typeof rid === 'string' && rid !== '') { if(typeof rid === "string" && rid !== "") {
reply_id = parseInt(rid, 10) reply_id = parseInt(rid,10)
if (isNaN(reply_id)) { if(isNaN(reply_id)) {
throw { throw {
statusCode: 414, statusCode: 414,
message: 'no valid reply id given', message: "no valid reply id given"
} }
} }
} }
if (typeof reply_id !== 'number') { if (typeof reply_id !== "number") {
throw { throw {
statusCode: 415, statusCode: 415,
message: 'no valid reply id given', message: "no valid reply id given"
} //backup case } //backup case
} }
return reply_id return reply_id
} }
function validateReceiver(rec) { function validateReceiver(rec) {
let receiver = encodeURIComponent(rec || '') let receiver = encodeURIComponent(rec || "");
if (receiver === '') receiver = 'everyone' if (receiver === "")
receiver = "everyone";
return receiver return receiver
} }
router.post('/api/post', async (req, res) => { router.post("/api/post", async (req, res) => {
try { try {
let message = validateMessage(req.body.message) let message = validateMessage(req.body.message);
validatePID(req.body.pid) validatePID(req.body.pid);
let reply_id = validateReplyID(req.body.reply_id) let reply_id = validateReplyID(req.body.reply_id);
let receiver = validateReceiver(req.body.receiver) let receiver = validateReceiver(req.body.receiver);
let __dirname = server.dirname let __dirname = server.dirname
const file_names = ['', '', '', '', ''] const file_names = ["","","","",""]
if (isNotNull(req.files)) { if(isNotNull(req.files)) {
for (let file_index = 0; file_index < 5; file_index++) { for(let file_index=0;file_index<5;file_index++) {
if (isNotNull(req.files[`file_${file_index}`])) { if(isNotNull(req.files[`file_${file_index}`])) {
let file = req.files[`file_${file_index}`] let file = req.files[`file_${file_index}`]
const file_id = server.genstring(20) const file_id = server.genstring(20)
const file_name = `${file_id}/${file.name.substring(0, 25).replace(/\.[^/.]+$/, '')}` const file_name = `${file_id}/${(file.name.substring(0,25)).replace(/\.[^/.]+$/, "")}`
let extension = file.name.substring( let extension = file.name.substring(file.name.lastIndexOf("\.")+1)
file.name.lastIndexOf('\.') + 1 file_names[file_index]=`${file_name}${(extension in image_types && ".webp") || extension}`
) server.ensureExists(`${__dirname}/user_uploads/${file_id}`,undefined,async (err)=>{
file_names[file_index] = if(err) {
`${file_name}${(extension in image_types && '.webp') || extension}`
server.ensureExists(
`${__dirname}/user_uploads/${file_id}`,
undefined,
async (err) => {
if (err) {
console.error(err)
return
}
if (extension in image_types) {
writeFile(
`${__dirname}/user_uploads/${file_name}.webp`,
await sharp(file.data)
.webp({ mixed: true, effort: 6 })
.toBuffer(),
(err2) => {
if (err2) console.error(err2)
}
)
server.ensureExists(
`${__dirname}/user_uploads/previews/${file_id}`,
undefined,
async (error) => {
if (error) {
console.error(error)
return
}
writeFile(
`${__dirname}/user_uploads/previews/${file_name}.webp`,
await sharp(file.data)
.resize(100, 100, {
fit: 'inside',
})
.webp({
mixed: true,
effort: 6,
})
.toBuffer(),
(error2) => {
if (error2)
console.error(error2)
}
)
}
)
} else {
file.mv(
`${__dirname}/user_uploads/${file_name}.${extension}`,
(err2) => {
if (err2) console.error(err2)
}
)
}
}
)
}
}
}
let sql = `START TRANSACTION;INSERT INTO ipost.posts (post_user_name,post_text,post_time,post_receiver_name,post_from_bot,post_reply_id,file_0,file_1,file_2,file_3,file_4) VALUES (?,?,?,?,?,?,?,?,?,?,?);SELECT LAST_INSERT_ID() as ID;COMMIT;`
let values = [
encodeURIComponent(res.locals.username),
message,
Date.now(),
receiver,
res.locals.isbot,
reply_id,
...file_names,
]
con.query(sql, values, function (err, result) {
if (err) {
res.status(500)
res.json({ error: "there's been an interal error" })
console.error(err) console.error(err)
return return;
} }
let post_obj = { if(extension in image_types) {
post_user_name: encodeURIComponent(res.locals.username), writeFile(`${__dirname}/user_uploads/${file_name}.webp`,await sharp(file.data).webp({mixed:true,effort:6}).toBuffer(),(err2)=>{
post_text: req.body.message, if(err2)console.error(err2)
post_time: Date.now(), })
post_special_text: '', server.ensureExists(`${__dirname}/user_uploads/previews/${file_id}`,undefined,async (error) => {
post_receiver_name: req.body.receiver, if(error) {
post_from_bot: res.locals.isbot, console.error(error)
post_reply_id: reply_id, return;
user_avatar: res.locals.avatar, }
files: file_names, writeFile(`${__dirname}/user_uploads/previews/${file_name}.webp`,await sharp(file.data).resize(100,100,{fit: "inside"}).webp({mixed:true,effort:6}).toBuffer(),(error2)=>{
post_id: result[0].ID, if(error2)console.error(error2)
} })
let message = { })
message: 'new_post', } else {
data: post_obj, file.mv(`${__dirname}/user_uploads/${file_name}.${extension}`,(err2)=>{
} if(err2)console.error(err2)
let messagestr = JSON.stringify(message) })
//console.log(5,server.wss.clients); /* DEBUG: Log websocket clients */ }
server.wss.clients.forEach(function (ws) {
//console.log(5,ws); /* DEBUG: Log websocket clients */
ws.send(messagestr)
}) })
res.json({ success: 'successfully posted message' }) }
console.log( }
5, }
`posted new message by ${res.locals.username} : ${req.body.message}`
) let sql = `START TRANSACTION;INSERT INTO ipost.posts (post_user_name,post_text,post_time,post_receiver_name,post_from_bot,post_reply_id,file_0,file_1,file_2,file_3,file_4) VALUES (?,?,?,?,?,?,?,?,?,?,?);SELECT LAST_INSERT_ID() as ID;COMMIT;`;
}) let values = [encodeURIComponent(res.locals.username), message, Date.now(), receiver, res.locals.isbot, reply_id,...file_names];
} catch (error) { con.query(sql, values, function (err, result) {
if (error.statusCode) { if (err){
res.status(error.statusCode)
res.json({ error: error.message, status: error.statusCode })
} else {
console.error('some error: ', error)
res.status(500) res.status(500)
res.json({ error: 'internal server error', status: 500 }) res.json({"error":"there's been an interal error"})
console.error(err)
return;
}
let post_obj = {
post_user_name: encodeURIComponent(res.locals.username),
post_text: req.body.message,
post_time: Date.now(),
post_special_text: "",
post_receiver_name: req.body.receiver,
post_from_bot: res.locals.isbot,
post_reply_id: reply_id,
user_avatar: res.locals.avatar,
files: file_names,
post_id: result[0].ID
};
let message = {
message: "new_post",
data: post_obj
};
let messagestr = JSON.stringify(message);
//console.log(5,server.wss.clients); /* DEBUG: Log websocket clients */
server.wss.clients.forEach(function(ws) {
//console.log(5,ws); /* DEBUG: Log websocket clients */
ws.send(messagestr);
});
res.json({ "success": "successfully posted message" });
console.log(5, `posted new message by ${res.locals.username} : ${req.body.message}`);
});
} catch (error) {
if(error.statusCode) {
res.status(error.statusCode)
res.json({ "error": error.message, "status": error.statusCode });
} else {
console.error("some error: ", error)
res.status(500)
res.json({"error":"internal server error", "status": 500})
} }
} }
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
return createPID return createPID
} };
export default { export default {
setup, setup
} };

View File

@ -1,44 +1,42 @@
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
router.get('/api/search', function (req, res) { router.get("/api/search", function (req, res) {
res.set('Access-Control-Allow-Origin', '') res.set("Access-Control-Allow-Origin", "");
let type = req.query.type let type = req.query.type;
let arg = encodeURIComponent(req.query.selector) let arg = encodeURIComponent(req.query.selector);
if (type === 'user') { if (type === "user") {
let sql = `select User_Name,User_Bio,User_Avatar from ipost.users where User_Name like ? limit 10;` let sql = `select User_Name,User_Bio,User_Avatar from ipost.users where User_Name like ? limit 10;`;
con.query(sql, [`%${arg}%`], function (err, result) { con.query(sql, [`%${arg}%`], function (err, result) {
if (err) throw err if (err)
throw err;
if (result[0]) { if (result[0]) {
result['message'] = result["message"] = "search has been deprecated as of 11/30/2022"
'search has been deprecated as of 11/30/2022' res.json(result);
res.json(result)
} else {
res.json({ error: 'there is no such user!' })
} }
}) else {
} else if (type === 'post') { res.json({ "error": "there is no such user!" });
let sql = `select post_user_name,post_text,post_time,post_special_text,post_id from ipost.posts where post_text like ? and (post_receiver_name is null or post_receiver_name = 'everyone') order by post_id desc limit 20;` }
});
}
else if (type === "post") {
let sql = `select post_user_name,post_text,post_time,post_special_text,post_id from ipost.posts where post_text like ? and (post_receiver_name is null or post_receiver_name = 'everyone') order by post_id desc limit 20;`;
con.query(sql, [`%${arg}%`], function (err, result) { con.query(sql, [`%${arg}%`], function (err, result) {
if (err) throw err if (err)
throw err;
if (result[0]) { if (result[0]) {
result['message'] = result["message"] = "search has been deprecated as of 11/30/2022"
'search has been deprecated as of 11/30/2022' res.json(result);
res.json(result)
} else {
res.json({
error: 'there is no such post!',
message: 'search has been deprecated as of 11/30/2022',
})
} }
}) else {
} else { res.json({ "error": "there is no such post!", "message": "search has been deprecated as of 11/30/2022"});
res.json({ }
error: 'invalid type passed along, expected `user` or `post`', });
message: 'search has been deprecated as of 11/30/2022', }
}) else {
res.json({ "error": "invalid type passed along, expected `user` or `post`", "message": "search has been deprecated as of 11/30/2022"});
} }
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
} }

View File

@ -1,71 +1,60 @@
const allowed_settings = { const allowed_settings = {
ACCR: ['boolean'], "ACCR": ["boolean"]
} };
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
router.get('/api/settings', function (req, res) { router.get("/api/settings", function (req, res) {
res.json(res.locals.settings) res.json(res.locals.settings);
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
router.post('/api/settings', function (req, res) { router.post("/api/settings", function (req, res) {
if (!req.body.setting) { if (!req.body.setting) {
res.status(410) res.status(410)
res.json({ error: 'no setting to change' }) res.json({ "error": "no setting to change" });
return return;
} }
if (typeof req.body.setting !== 'string') { if ((typeof req.body.setting) !== "string") {
res.status(411) res.status(411)
res.json({ error: 'no setting to change' }) res.json({ "error": "no setting to change" });
return return;
} }
let types = allowed_settings[req.body.setting] let types = allowed_settings[req.body.setting];
let allowed = false let allowed = false;
let got = typeof req.body.value let got = typeof req.body.value;
for (let index = 0; index < types.length; index++) { for (let index = 0; index < types.length; index++) {
if (types[index] === got) { if (types[index] === got) {
allowed = true allowed = true;
break break;
} }
} }
if (!allowed) { if (!allowed) {
console.log( console.log(5, "incorrect type given, received, expected", typeof req.body.value, allowed_settings[req.body.setting]);
5,
'incorrect type given, received, expected',
typeof req.body.value,
allowed_settings[req.body.setting]
)
res.status(412) res.status(412)
res.json({ error: 'no new setting value given' }) res.json({ "error": "no new setting value given" });
return return;
} }
let setting_to_change = req.body.setting let setting_to_change = req.body.setting;
let setting_new_value = req.body.value let setting_new_value = req.body.value;
res.locals.settings[setting_to_change] = setting_new_value res.locals.settings[setting_to_change] = setting_new_value;
console.log( console.log(5, "changing settings", setting_to_change, setting_new_value, res.locals.settings);
5, let sql = "update ipost.users set User_Settings=? where User_Name=?";
'changing settings', let values = [JSON.stringify(res.locals.settings), res.locals.username];
setting_to_change,
setting_new_value,
res.locals.settings
)
let sql = 'update ipost.users set User_Settings=? where User_Name=?'
let values = [JSON.stringify(res.locals.settings), res.locals.username]
con.query(sql, values, function (err, result) { con.query(sql, values, function (err, result) {
if (err) { if (err) {
res.status(500) res.status(500)
res.json({ status: 'error', code: err }) res.json({ "status": "error", "code": err });
return return;
} }
res.json({ status: 'success' }) res.json({ "status": "success" });
}) });
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
} };
export default { export default {
setup, setup
} };

View File

@ -1,293 +1,234 @@
import sharp from 'sharp' import sharp from "sharp"
import { ensureExists } from '../../extra_modules/ensureExists.js' import { ensureExists } from "../../extra_modules/ensureExists.js"
import { SHA256 } from '../../extra_modules/SHA.js' import {SHA256} from "../../extra_modules/SHA.js";
import getIP from '../../extra_modules/getip.js' import getIP from "../../extra_modules/getip.js";
import { getunsigned } from '../../extra_modules/unsign.js' import {getunsigned} from "../../extra_modules/unsign.js";
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
const config = server.config const config = server.config
const HASHES_DB = config.cookies.server_hashes const HASHES_DB = config.cookies.server_hashes;
const HASHES_COOKIE = config.cookies.client_hashes const HASHES_COOKIE = config.cookies.client_hashes;
const HASHES_DIFF = HASHES_DB - HASHES_COOKIE const HASHES_DIFF = HASHES_DB - HASHES_COOKIE;
const __dirname = server.dirname router.post("/api/setavatar", function (req, res) {
res.set("Access-Control-Allow-Origin", "");
router.post('/api/setavatar', function (req, res) {
res.set('Access-Control-Allow-Origin', '')
if (!req.files || Object.keys(req.files).length === 0) { if (!req.files || Object.keys(req.files).length === 0) {
return res.status(410).send('No files were uploaded. (req.files)') return res.status(410).send('No files were uploaded. (req.files)');
} }
let avatar = req.files.avatar let avatar = req.files.avatar;
if (!avatar) { if (!avatar) {
return res.status(411).send('No files were uploaded. (req.files.)') return res.status(411).send('No files were uploaded. (req.files.)');
} }
const avatars = __dirname + '/avatars/' const avatars = __dirname + '/avatars/';
ensureExists(avatars, function (err) { ensureExists(avatars, function (err) {
if (err) { if (err) {
return res return res.status(500).json({ "error": "there's been an internal server error." });
.status(500)
.json({ error: "there's been an internal server error." })
} }
if (res.locals.avatar) { if (res.locals.avatar) {
try { try {
unlinkSync(avatars + res.locals.avatar) unlinkSync(avatars + res.locals.avatar);
} catch (ignored) {} } catch(ignored){}
} }
let filename = genstring(95) + '.webp' let filename = genstring(95) + ".webp";
while ( while (existsSync(avatars + "/" + filename) || filename === ".webp") { //generate new filename until it's unique
existsSync(avatars + '/' + filename) || filename = genstring(95) + ".webp";
filename === '.webp'
) {
//generate new filename until it's unique
filename = genstring(95) + '.webp'
} }
sharp(avatar.data) sharp(avatar.data).resize({ //resize avatar to 100x100 and convert it to a webp, then store it
.resize({ width: 100,
//resize avatar to 100x100 and convert it to a webp, then store it height: 100
width: 100, }).webp({
height: 100, effort: 6,
}) mixed: true
.webp({ }).toBuffer().then(function(data){
effort: 6, writeFileSync(avatars + filename,data)
mixed: true, let sql = `update ipost.users set User_Avatar=? where User_Name=?`;
}) con.query(sql, [filename, encodeURIComponent(res.locals.username)], function (err) {
.toBuffer() if (err)
.then(function (data) { throw err;
writeFileSync(avatars + filename, data) res.json({ "success": "updated avatar" });
let sql = `update ipost.users set User_Avatar=? where User_Name=?` });
con.query( })
sql, });
[filename, encodeURIComponent(res.locals.username)],
function (err) {
if (err) throw err
res.json({ success: 'updated avatar' })
}
)
})
})
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
router.get('/api/getuser', function (_req, res) { router.get("/api/getuser", function (_req, res) {
res.json({ res.json({ "username": res.locals.username, "bio": res.locals.bio, "avatar": res.locals.avatar, "userid": res.locals.userid });
username: res.locals.username,
bio: res.locals.bio,
avatar: res.locals.avatar,
userid: res.locals.userid,
})
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
router.get('/api/getalluserinformation', function (req, res) { router.get("/api/getalluserinformation", function (req, res) {
res.set('Access-Control-Allow-Origin', '') //we don't want that here res.set("Access-Control-Allow-Origin", ""); //we don't want that here
let unsigned = getunsigned(req, res) //has to be asking for it via the cookie let unsigned = getunsigned(req, res); //has to be asking for it via the cookie
if (!unsigned) return if (!unsigned)
unsigned = decodeURIComponent(unsigned) return;
let sql = `select * from ipost.users where User_Name=? and User_PW=?;` unsigned = decodeURIComponent(unsigned);
let values = unsigned.split(' ') let sql = `select * from ipost.users where User_Name=? and User_PW=?;`;
values[1] = SHA256(values[1], values[0], HASHES_DIFF) let values = unsigned.split(" ");
values[1] = SHA256(values[1], values[0], HASHES_DIFF);
con.query(sql, values, function (err, result) { con.query(sql, values, function (err, result) {
if (err) throw err if (err)
throw err;
if (result[0]) { if (result[0]) {
res.status(200) res.status(200);
res.json(result[0]) res.json(result[0]);
} else {
res.status(402)
res.json({
error: 'you cannot access the api without being logged in',
})
} }
}) else {
res.status(402);
res.json({ "error": "you cannot access the api without being logged in" });
}
});
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
router.get('/api/getotheruser', function (req, res) { router.get("/api/getotheruser", function (req, res) {
res.set('Access-Control-Allow-Origin', '*') res.set("Access-Control-Allow-Origin", "*");
let username = req.query.user let username = req.query.user;
let sql = `select User_Name,User_Bio,User_Avatar from ipost.users where User_Name=?;` let sql = `select User_Name,User_Bio,User_Avatar from ipost.users where User_Name=?;`;
con.query(sql, [username], function (err, result) { con.query(sql, [username], function (err, result) {
if (err) throw err if (err)
throw err;
if (result[0]) { if (result[0]) {
res.json({ res.json({ "username": username, "bio": result[0].User_Bio, "avatar": result[0].User_Avatar, "publicKey": result[0].User_PublicKey });
username: username,
bio: result[0].User_Bio,
avatar: result[0].User_Avatar,
publicKey: result[0].User_PublicKey,
})
} else {
res.json({ error: 'there is no such user!' })
} }
}) else {
}) res.json({ "error": "there is no such user!" });
router.post('/api/setBio', function (req, res) { }
res.set('Access-Control-Allow-Origin', '') });
let bio = req.body.Bio });
router.post("/api/setBio", function (req, res) {
res.set("Access-Control-Allow-Origin", "");
let bio = req.body.Bio;
if (!bio) { if (!bio) {
res.status(410) res.status(410);
res.json({ error: 'no bio set!' }) res.json({ "error": "no bio set!" });
return return;
} }
bio = encodeURIComponent(bio) bio = encodeURIComponent(bio);
if (bio.length > 100) { if (bio.length > 100) {
res.status(411) res.status(411);
res.json({ error: 'the bio is too long!' }) res.json({ "error": "the bio is too long!" });
return return;
} }
let sql = `update ipost.users set User_Bio=? where User_Name=?` let sql = `update ipost.users set User_Bio=? where User_Name=?`;
con.query( con.query(sql, [bio, encodeURIComponent(res.locals.username)], function (err) {
sql, if (err)
[bio, encodeURIComponent(res.locals.username)], throw err;
function (err) { res.json({ "success": "updated bio" });
if (err) throw err });
res.json({ success: 'updated bio' })
}
)
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
router.post('/api/changePW', (req, res) => { router.post("/api/changePW", (req, res) => {
res.set('Access-Control-Allow-Origin', '') res.set("Access-Control-Allow-Origin", "");
if (typeof req.body.newPW !== 'string') { if ((typeof req.body.newPW) !== "string") {
res.json({ error: 'incorrect password' }) res.json({ "error": "incorrect password" });
return return;
} }
if (typeof req.body.currentPW !== 'string') { if ((typeof req.body.currentPW) !== "string") {
res.json({ error: 'incorrect password' }) res.json({ "error": "incorrect password" });
return return;
} }
if (req.body.newPW.length < 10) { if (req.body.newPW.length < 10) {
res.status(410) res.status(410);
res.json({ error: 'password is too short' }) res.json({ "error": "password is too short" });
return return;
} }
let hashed_pw = SHA256( let hashed_pw = SHA256(req.body.currentPW, res.locals.username, HASHES_DB);
req.body.currentPW, let hashed_new_pw = SHA256(req.body.newPW, res.locals.username, HASHES_DB);
res.locals.username, let sql = `select * from ipost.users where User_Name=? and User_PW=?;`;
HASHES_DB let values = [res.locals.username, hashed_pw];
)
let hashed_new_pw = SHA256(
req.body.newPW,
res.locals.username,
HASHES_DB
)
let sql = `select * from ipost.users where User_Name=? and User_PW=?;`
let values = [res.locals.username, hashed_pw]
con.query(sql, values, function (err, result) { con.query(sql, values, function (err, result) {
if (err) throw err if (err)
throw err;
if (result[0]) { if (result[0]) {
let sql = `update ipost.users set User_PW=? where User_Name=? and User_PW=?;` let sql = `update ipost.users set User_PW=? where User_Name=? and User_PW=?;`;
let values = [hashed_new_pw, res.locals.username, hashed_pw] let values = [hashed_new_pw, res.locals.username, hashed_pw];
con.query(sql, values, (err2) => { con.query(sql, values, (err2) => {
if (err2) throw err2 if (err2)
let ip = getIP(req) throw err2;
let ip = getIP(req);
let setTo = `${res.locals.username} ${SHA256(req.body.newPW, res.locals.username, HASHES_COOKIE)}` let setTo = `${res.locals.username} ${SHA256(req.body.newPW, res.locals.username, HASHES_COOKIE)}`
let cookiesigned = signature.sign(setTo, cookiesecret + ip) let cookiesigned = signature.sign(setTo, cookiesecret + ip);
res.cookie('AUTH_COOKIE', cookiesigned, { res.cookie('AUTH_COOKIE', cookiesigned, { maxAge: Math.pow(10, 10), httpOnly: true, secure: true });
maxAge: Math.pow(10, 10), res.json({ "success": "successfully changed password" });
httpOnly: true, });
secure: true,
})
res.json({ success: 'successfully changed password' })
})
} else {
res.json({ error: 'invalid password' })
} }
}) else {
res.json({ "error": "invalid password" });
}
});
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
router.post('/api/changeUsername', function (req, res) { router.post("/api/changeUsername", function (req, res) {
res.set('Access-Control-Allow-Origin', '') res.set("Access-Control-Allow-Origin", "");
if (typeof req.body.newUsername !== 'string') { if ((typeof req.body.newUsername) !== "string") {
res.status(410) res.status(410);
res.json({ error: 'incorrect username' }) res.json({ "error": "incorrect username" });
return return;
} }
if (typeof req.body.currentPW !== 'string') { if ((typeof req.body.currentPW) !== "string") {
res.status(411) res.status(411);
res.json({ error: 'incorrect password' }) res.json({ "error": "incorrect password" });
return return;
} }
if (req.body.newUsername.length > 100) { if (req.body.newUsername.length > 100) {
res.status(412) res.status(412);
res.json({ error: 'username is too long' }) res.json({ "error": "username is too long" });
return return;
} }
if (req.body.newUsername === res.locals.username) { if (req.body.newUsername === res.locals.username) {
res.status(413) res.status(413);
res.json({ error: "username can't be the current one" }) res.json({ "error": "username can't be the current one" });
return return;
} }
let hashed_pw = SHA256( let hashed_pw = SHA256(req.body.currentPW, res.locals.username, HASHES_DB);
req.body.currentPW, let hashed_new_pw = SHA256(req.body.currentPW, req.body.newUsername, HASHES_DB);
res.locals.username, let sql = `select * from ipost.users where User_Name=? and User_PW=?;`; //check if pw is correct
HASHES_DB let values = [res.locals.username,hashed_pw];
)
let hashed_new_pw = SHA256(
req.body.currentPW,
req.body.newUsername,
HASHES_DB
)
let sql = `select * from ipost.users where User_Name=? and User_PW=?;` //check if pw is correct
let values = [res.locals.username, hashed_pw]
con.query(sql, values, function (err, result) { con.query(sql, values, function (err, result) {
if (err) throw err if (err)
throw err;
if (result[0]) { if (result[0]) {
let sql = `select * from ipost.users where User_Name=?;` //check if newUsername isn't already used let sql = `select * from ipost.users where User_Name=?;`; //check if newUsername isn't already used
let values = [req.body.newUsername] let values = [req.body.newUsername];
con.query(sql, values, function (err, result) { con.query(sql, values, function (err, result) {
if (err) throw err if (err)
throw err;
if (result[0]) { if (result[0]) {
res.json({ res.json({ "error": "user with that username already exists" });
error: 'user with that username already exists', return;
})
return
} }
let sql = `update ipost.users set User_PW=?,User_Name=? where User_Name=? and User_PW=?;` //change username in users let sql = `update ipost.users set User_PW=?,User_Name=? where User_Name=? and User_PW=?;`; //change username in users
let values = [ let values = [hashed_new_pw, req.body.newUsername, res.locals.username, hashed_pw];
hashed_new_pw,
req.body.newUsername,
res.locals.username,
hashed_pw,
]
con.query(sql, values, function (err) { con.query(sql, values, function (err) {
if (err) throw err if (err)
let ip = getIP(req) throw err;
let ip = getIP(req);
let setTo = `${req.body.newUsername} ${SHA256(req.body.currentPW, req.body.newUsername, HASHES_COOKIE)}` let setTo = `${req.body.newUsername} ${SHA256(req.body.currentPW, req.body.newUsername, HASHES_COOKIE)}`
let cookiesigned = signature.sign( let cookiesigned = signature.sign(setTo, cookiesecret + ip);
setTo, res.cookie('AUTH_COOKIE', cookiesigned, { maxAge: Math.pow(10, 10), httpOnly: true, secure: true });
cookiesecret + ip
)
res.cookie('AUTH_COOKIE', cookiesigned, {
maxAge: Math.pow(10, 10),
httpOnly: true,
secure: true,
})
//updated username in the users table, but not yet on posts //updated username in the users table, but not yet on posts
//TODO: update username on dms //TODO: update username on dms
let sql = `update ipost.posts set post_user_name=? where post_user_name=?;` //change username of every past post sent let sql = `update ipost.posts set post_user_name=? where post_user_name=?;`; //change username of every past post sent
let values = [ let values = [req.body.newUsername, res.locals.username, hashed_pw];
req.body.newUsername,
res.locals.username,
hashed_pw,
]
con.query(sql, values, () => { con.query(sql, values, () => {
res.json({ res.json({ "success": "successfully changed username" }); //done
success: 'successfully changed username', });
}) //done });
}) });
})
})
} else {
res.json({ error: 'invalid password' })
} }
}) else {
res.json({ "error": "invalid password" });
}
});
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
} }

View File

@ -1,72 +1,59 @@
import { randomBytes } from 'crypto' import {randomBytes} from "crypto"
import { SHA256 } from '../extra_modules/SHA.js' import {SHA256} from "../extra_modules/SHA.js";
import { unsign } from '../extra_modules/unsign.js' import {unsign} from "../extra_modules/unsign.js";
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
const temp_code_to_token = {} const temp_code_to_token = {}
router.post('/authorize', async (req, res) => { router.post("/authorize",async (req,res) => {
if (!unsign(req.cookies.AUTH_COOKIE, req, res)) { if (!unsign(req.cookies.AUTH_COOKIE, req, res)){
return return
} }
let data = await server.hcaptcha.verify(req.body['h-captcha-response']) let data = await server.hcaptcha.verify(req.body["h-captcha-response"])
if(data.success) {
if (data.success) {
let appid = req.body.application_id let appid = req.body.application_id
if (typeof appid === 'string') { if(typeof appid === "string") {
appid = Number(appid) appid = Number(appid)
} }
if (typeof appid === 'number') { if(typeof appid === "number") {
const token = randomBytes(150).toString('base64')
let tokencode const token = randomBytes(150).toString("base64")
while (
tokencode === undefined || let tokencode;
temp_code_to_token[tokencode] !== undefined while(tokencode===undefined || temp_code_to_token[tokencode]!==undefined) {
) { tokencode = randomBytes(15).toString("base64").replaceAll("/","f").replaceAll("+","A") //"/" and "+" may break some apps
tokencode = randomBytes(15)
.toString('base64')
.replaceAll('/', 'f')
.replaceAll('+', 'A') //"/" and "+" may break some apps
} }
temp_code_to_token[tokencode] = { temp_code_to_token[tokencode]={
userid: res.locals.userid, "userid":res.locals.userid,
appid: appid, "appid":appid,
token: token, "token":token
} }
setTimeout( setTimeout(() => {
() => { let data = temp_code_to_token[tokencode]
let data = temp_code_to_token[tokencode] if(data !== undefined && data.token===token && data.appid === appid && data.userid === res.locals.userid) {
if ( temp_code_to_token[tokencode]=undefined
data !== undefined && }
data.token === token && }, 1000*60*5);
data.appid === appid &&
data.userid === res.locals.userid
) {
temp_code_to_token[tokencode] = undefined
}
},
1000 * 60 * 5
)
const sql = const sql = "SELECT application_auth_url FROM ipost.application where application_id=?"
'SELECT application_auth_url FROM ipost.application where application_id=?'
con.query(sql, [appid], (err, result) => { con.query(sql,[appid],(err,result) => {
if (err || result.length !== 1) { if(err || result.length !== 1) {
console.err(err) console.err(err)
res.redirect(`/authorize?id=${req.body.application_id}`) res.redirect(`/authorize?id=${req.body.application_id}`)
return return
} }
let extra = '' let extra = ""
if (req.body.application_extra !== '') { if(req.body.application_extra !== "") {
extra = '&extra=' + String(req.body.application_extra) extra = "&extra="+String(req.body.application_extra)
} }
res.redirect( res.redirect(`${result[0].application_auth_url}?code=${tokencode}${extra}`)
`${result[0].application_auth_url}?code=${tokencode}${extra}`
)
}) })
return return
} }
} }
@ -78,76 +65,68 @@ export const setup = function (router, con, server) {
}] */ }] */
}) })
router.post('/redeemauthcode', (req, res) => { router.post("/redeemauthcode", (req,res) => {
if (temp_code_to_token[req.body.authcode] === undefined) {
if(temp_code_to_token[req.body.authcode]===undefined) {
res.status(400) res.status(400)
res.json({ status: 400, message: 'invalid code given' }) res.json({"status":400,"message":"invalid code given"})
return return
} }
if (typeof req.body.auth === 'string') { if(typeof req.body.auth === "string") {
try { try{
req.body.auth = JSON.parse(req.body.auth) req.body.auth = JSON.parse(req.body.auth)
} catch (err) { } catch(err) {
console.log('error parsing', err) console.log("error parsing",err)
} }
} }
if ( if(
typeof req.body.auth !== 'object' || typeof req.body.auth !== "object" ||
typeof req.body.auth.secret !== 'string' || typeof req.body.auth.secret !== "string" ||
typeof req.body.auth.appid !== 'number' || typeof req.body.auth.appid !== "number" ||
req.body.auth.secret.length !== 200 || req.body.auth.secret.length !== 200 ||
Buffer.from(req.body.auth.secret, 'base64').length !== 150 || Buffer.from(req.body.auth.secret,"base64").length !== 150 ||
req.body.auth.appid !== temp_code_to_token[req.body.authcode].appid req.body.auth.appid !== temp_code_to_token[req.body.authcode].appid
) { ) {
//console.log(1,req.body.auth,temp_code_to_token[req.body.authcode].appid) //console.log(1,req.body.auth,temp_code_to_token[req.body.authcode].appid)
res.status(420).send('invalid authentication object') res.status(420).send("invalid authentication object")
return return;
} }
const appid = req.body.auth.appid const appid = req.body.auth.appid
const checksecret = SHA256(req.body.auth.secret, appid, 10000) const checksecret = SHA256(req.body.auth.secret,appid,10000)
const checksql = const checksql = "SELECT application_id from ipost.application where application_secret=? and application_id=?"
'SELECT application_id from ipost.application where application_secret=? and application_id=?' const checkvalues = [checksecret,appid]
const checkvalues = [checksecret, appid]
con.query(checksql, checkvalues, (error, result_object) => { con.query(checksql,checkvalues,(error,result_object) => {
if (
error || if(error || result_object[0]===undefined || result_object[0].application_id!==appid) {
result_object[0] === undefined ||
result_object[0].application_id !== appid
) {
res.status(400) res.status(400)
res.json({ status: 400, message: 'invalid code given' }) res.json({"status":400,"message":"invalid code given"})
return return
} }
let data = temp_code_to_token[req.body.authcode] let data = temp_code_to_token[req.body.authcode]
temp_code_to_token[req.body.authcode] = undefined temp_code_to_token[req.body.authcode] = undefined
const sql =
'INSERT INTO `ipost`.`auth_tokens`(`auth_token`,`auth_token_u_id`,`auth_token_isfrom_application_id`) VALUES(?,?,?);'
const values = [ const sql = "INSERT INTO `ipost`.`auth_tokens`(`auth_token`,`auth_token_u_id`,`auth_token_isfrom_application_id`) VALUES(?,?,?);"
SHA256(data.token, appid, 10000),
data.userid, const values = [SHA256(data.token,appid,10000),data.userid,data.appid] //token,id,appid
data.appid, con.query(sql,values,(err,result) => {
] //token,id,appid if(err) {
con.query(sql, values, (err, result) => { res.json({"status":500,"message":"error redeeming code"})
if (err) {
res.json({ status: 500, message: 'error redeeming code' })
console.err(err) console.err(err)
} else { } else {
res.json({ res.json({"status":200,"message":"successfully redeemed code","token":data.token})
status: 200,
message: 'successfully redeemed code',
token: data.token,
})
} }
}) })
}) })
}) })
/* #swagger.security = [{ /* #swagger.security = [{

View File

@ -1,13 +1,10 @@
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
const increaseUSERCall = server.increaseUSERCall const increaseUSERCall = server.increaseUSERCall
router.get('/logout', function (req, res) { router.get("/logout", function (req, res) {
if (!increaseUSERCall(req, res)) return if (!increaseUSERCall(req, res))return;
res.cookie('AUTH_COOKIE', '', { res.cookie("AUTH_COOKIE", "", { maxAge: 0, httpOnly: true, secure: true });
maxAge: 0, res.redirect("/");
httpOnly: true, });
secure: true,
})
res.redirect('/')
})
} }

View File

@ -1,73 +1,74 @@
import { existsSync } from 'fs' import {existsSync} from "fs"
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
const increaseUSERCall = server.increaseUSERCall const increaseUSERCall = server.increaseUSERCall
const __dirname = server.dirname const __dirname = server.dirname
const dir = __dirname + '/' const dir = __dirname + "/"
router.get('/users/:user', function (req, res) { router.get("/users/:user", function (req, res) {
if (!increaseUSERCall(req, res)) return if (!increaseUSERCall(req, res))
res.sendFile(dir + 'views/otheruser.html') return;
}) res.sendFile(dir + "views/otheruser.html");
router.get('/css/:file', (request, response) => { });
if (!increaseUSERCall(request, response)) return router.get("/css/:file", (request, response) => {
if (!increaseUSERCall(request, response))
return;
if (existsSync(`${__dirname}/css/${request.params.file}`)) { if (existsSync(`${__dirname}/css/${request.params.file}`)) {
response.sendFile(`${__dirname}/css/${request.params.file}`) response.sendFile(`${__dirname}/css/${request.params.file}`);
} else {
response.status(404).send('no file with that name found')
} }
return else {
}) response.status(404).send("no file with that name found");
router.get('/js/:file', (request, response) => { }
if (!increaseUSERCall(request, response)) return return;
});
router.get("/js/:file", (request, response) => {
if (!increaseUSERCall(request, response))
return;
if (existsSync(`${__dirname}/js/${request.params.file}`)) { if (existsSync(`${__dirname}/js/${request.params.file}`)) {
response.sendFile(`${__dirname}/js/${request.params.file}`) response.sendFile(`${__dirname}/js/${request.params.file}`);
} else {
response.status(404).send('no file with that name found')
} }
return else {
}) response.status(404).send("no file with that name found");
router.get('/images/:file', (request, response) => { }
if (!increaseUSERCall(request, response)) return return;
});
router.get("/images/:file", (request, response) => {
if (!increaseUSERCall(request, response))
return;
if (existsSync(`${__dirname}/images/${request.params.file}`)) { if (existsSync(`${__dirname}/images/${request.params.file}`)) {
response.set('Cache-Control', 'public, max-age=2592000') //cache it for one month-ish response.set('Cache-Control', 'public, max-age=2592000'); //cache it for one month-ish
response.sendFile(`${__dirname}/images/${request.params.file}`) response.sendFile(`${__dirname}/images/${request.params.file}`);
} else if (
existsSync(
`${__dirname}/images/${request.params.file.toLowerCase()}`
)
) {
response.set('Cache-Control', 'public, max-age=2592000') //cache it for one month-ish
response.sendFile(
`${__dirname}/images/${request.params.file.toLowerCase()}`
)
} else {
response.status(404).send('no file with that name found')
} }
return else if(existsSync(`${__dirname}/images/${request.params.file.toLowerCase()}`)){
}) response.set('Cache-Control', 'public, max-age=2592000'); //cache it for one month-ish
response.sendFile(`${__dirname}/images/${request.params.file.toLowerCase()}`);
}
else {
response.status(404).send("no file with that name found");
}
return;
});
router.get('/user_uploads/:file', (request, response) => { router.get("/user_uploads/:file", (request, response) => {
if (!increaseUSERCall(request, response)) return if (!increaseUSERCall(request, response))
return;
if (existsSync(`${__dirname}/user_uploads/${request.params.file}`)) { if (existsSync(`${__dirname}/user_uploads/${request.params.file}`)) {
response.set('Cache-Control', 'public, max-age=2592000') //cache it for one month-ish response.set('Cache-Control', 'public, max-age=2592000'); //cache it for one month-ish
response.sendFile( response.sendFile(`${__dirname}/user_uploads/${request.params.file}`);
`${__dirname}/user_uploads/${request.params.file}`
)
} else {
response.status(404).send('no file with that name found')
} }
return else {
}) response.status(404).send("no file with that name found");
}
return;
});
router.get('/avatars/:avatar', (request, response) => { router.get("/avatars/:avatar", (request, response) => {
if (!increaseUSERCall(request, response)) return if (!increaseUSERCall(request, response))
response.set('Cache-Control', 'public, max-age=2592000') //cache it for one month-ish return;
response.set('Cache-Control', 'public, max-age=2592000'); //cache it for one month-ish
if (existsSync(`${__dirname}/avatars/${request.params.avatar}`)) { if (existsSync(`${__dirname}/avatars/${request.params.avatar}`)) {
return response.sendFile( return response.sendFile(`${__dirname}/avatars/${request.params.avatar}`);
`${__dirname}/avatars/${request.params.avatar}`
)
} }
response.status(404).send('No avatar with that name found') response.status(404).send("No avatar with that name found");
}) });
} }

View File

@ -1,28 +1,29 @@
import { setup as optionssetup } from './api/options.js' import { setup as optionssetup } from "./api/options.js";
import { setup as allsetup } from './api/all.js' import { setup as allsetup } from "./api/all.js";
import { setup as settingshandlersetup } from './api/settingshandler.js' import { setup as settingshandlersetup } from "./api/settingshandler.js";
import { setup as postsetup } from './api/post.js' import { setup as postsetup } from "./api/post.js";
import { setup as dmsPersonalMessagessetup } from './api/dms/PersonalMessages.js' import { setup as dmsPersonalMessagessetup } from "./api/dms/PersonalMessages.js";
import { setup as dmspostsetup } from './api/dms/post.js' import { setup as dmspostsetup } from "./api/dms/post.js";
import { setup as fileiconsetup } from './api/getFileIcon.js' import { setup as fileiconsetup } from "./api/getFileIcon.js";
import { setup as searchsetup } from './api/search.js' import { setup as searchsetup } from "./api/search.js";
import { setup as getpostssetup } from './api/getPosts.js' import { setup as getpostssetup } from "./api/getPosts.js";
import { setup as userroutessetup } from './api/userRoutes.js' import { setup as userroutessetup } from "./api/userRoutes.js";
import { setup as servefilessetup } from './serve_static_files.js' import { setup as servefilessetup} from "./serve_static_files.js"
import { setup as userfilessetup } from './userfiles.js' import { setup as userfilessetup} from "./userfiles.js"
import { setup as userauthsetup } from './user_auth.js' import { setup as userauthsetup} from "./user_auth.js"
import { setup as applicationsetup } from './authorize.js' import { setup as applicationsetup} from "./authorize.js"
import { setup as logoutsetup } from './logout.js' import { setup as logoutsetup} from "./logout.js"
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
const setuproute = (handler) => handler(router, con, server) const setuproute = handler => handler(router,con,server)
setuproute(optionssetup) setuproute(optionssetup)
setuproute(allsetup) setuproute(allsetup)
setuproute(settingshandlersetup) setuproute(settingshandlersetup)
const get_pid = setuproute(postsetup) const get_pid = setuproute(postsetup);
setuproute(dmsPersonalMessagessetup) setuproute(dmsPersonalMessagessetup)
const get_dmpid = setuproute(dmspostsetup) const get_dmpid = setuproute(dmspostsetup);
setuproute(fileiconsetup) setuproute(fileiconsetup)
setuproute(searchsetup) setuproute(searchsetup)
setuproute(getpostssetup) setuproute(getpostssetup)

View File

@ -1,200 +1,172 @@
import { SHA256 } from '../extra_modules/SHA.js' import {SHA256} from "../extra_modules/SHA.js";
import * as signature from 'cookie-signature' import * as signature from "cookie-signature";
import getIP from '../extra_modules/getip.js' import getIP from "../extra_modules/getip.js";
import { readFileSync } from 'fs' import {readFileSync} from "fs"
const cookiesecret = readFileSync('cookiesecret.txt').toString() const cookiesecret = readFileSync("cookiesecret.txt").toString();
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
const config = server.config const config = server.config
const DID_I_FINALLY_ADD_HTTPS = server.DID_I_FINALLY_ADD_HTTPS const DID_I_FINALLY_ADD_HTTPS = server.DID_I_FINALLY_ADD_HTTPS
const increaseAPICall = server.increaseAPICall const increaseAPICall = server.increaseAPICall
const HASHES_DB = config.cookies.server_hashes const HASHES_DB = config.cookies.server_hashes;
const HASHES_COOKIE = config.cookies.client_hashes const HASHES_COOKIE = config.cookies.client_hashes;
const HASHES_DIFF = HASHES_DB - HASHES_COOKIE const HASHES_DIFF = HASHES_DB - HASHES_COOKIE;
router.post('/register', function (req, res) { router.post("/register", function (req, res) {
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) { //don't want people spam registering
//don't want people spam registering if (!increaseAPICall(req, res))
if (!increaseAPICall(req, res)) return return;
} }
res.status(200) res.status(200);
if (typeof req.body.user !== 'string') { if ((typeof req.body.user) !== "string") {
res.status(416) res.status(416);
res.json({ error: 'incorrect username' }) res.json({ "error": "incorrect username" });
return return;
} }
if (typeof req.body.pass !== 'string') { if ((typeof req.body.pass) !== "string") {
res.status(417) res.status(417);
res.json({ error: 'incorrect password' }) res.json({ "error": "incorrect password" });
return return;
} }
let username = req.body.user.toString() let username = req.body.user.toString();
username = username.replace(/\s/gi, '') username = username.replace(/\s/gi, "");
let password = req.body.pass.toString() let password = req.body.pass.toString();
if (!username) { if (!username) {
res.status(410) res.status(410);
res.redirect('/register?success=false&reason=username') res.redirect("/register?success=false&reason=username");
return return;
} }
if (username === '') { if (username === "") {
res.status(411) res.status(411);
res.redirect('/register?success=false&reason=username') res.redirect("/register?success=false&reason=username");
return return;
} }
if (password.length < 10) { if (password.length < 10) {
res.status(412) res.status(412);
res.send('password is too short') res.send("password is too short");
return return;
} }
if (username.length > 25) { if (username.length > 25) {
res.status(413) res.status(413);
res.send('username is too long') res.send("username is too long");
return return;
} }
if (username.search('@') !== -1) { if (username.search("@") !== -1) {
res.status(414) res.status(414);
res.send("username can't contain @-characters") res.send("username can't contain @-characters");
return return;
} }
if (!password) { if (!password) {
res.status(415) res.status(415);
res.redirect('/register?success=false&reason=password') res.redirect("/register?success=false&reason=password");
return return;
} }
let userexistssql = `SELECT User_Name from ipost.users where User_Name = ?` let userexistssql = `SELECT User_Name from ipost.users where User_Name = ?`;
con.query( con.query(userexistssql, [encodeURIComponent(username)], function (_error, result) {
userexistssql, if (result && result[0] && result[0].User_Name) {
[encodeURIComponent(username)], res.status(418);
function (_error, result) { res.redirect("/register?success=false&reason=already_exists");
if (result && result[0] && result[0].User_Name) { return;
res.status(418)
res.redirect(
'/register?success=false&reason=already_exists'
)
return
}
let less_hashed_pw = SHA256(password, username, HASHES_DIFF)
let hashed_pw = SHA256(less_hashed_pw, username, HASHES_COOKIE)
let ip = getIP(req)
let setTo = `${username} ${SHA256(password, username, HASHES_COOKIE)}`
let cookiesigned = signature.sign(setTo, cookiesecret + ip)
ip = SHA256(ip, setTo, HASHES_DB)
const default_settings = {}
let values = [
encodeURIComponent(username),
hashed_pw,
Date.now(),
ip,
ip,
JSON.stringify(default_settings),
]
let sql = `INSERT INTO ipost.users (User_Name, User_PW, User_CreationStamp, User_CreationIP, User_LastIP, User_Settings) VALUES (?, ?, ?, ?, ?, ?);`
con.query(sql, values, function (err) {
if (err) throw err
res.cookie('AUTH_COOKIE', cookiesigned, {
maxAge: Math.pow(10, 10),
httpOnly: true,
secure: DID_I_FINALLY_ADD_HTTPS,
})
if (req.body.r !== undefined) {
res.redirect(decodeURIComponent(req.body.r))
} else {
res.redirect('/user')
}
})
} }
) let less_hashed_pw = SHA256(password, username, HASHES_DIFF);
}) let hashed_pw = SHA256(less_hashed_pw, username, HASHES_COOKIE);
router.post('/login', function (req, res) { let ip = getIP(req);
if (!increaseAPICall(req, res)) return let setTo = `${username} ${SHA256(password, username, HASHES_COOKIE)}`
if (typeof req.body.user !== 'string') { let cookiesigned = signature.sign(setTo, cookiesecret + ip);
res.status(416) ip = SHA256(ip, setTo, HASHES_DB);
res.json({ error: 'incorrect username' }) const default_settings = {};
return let values = [encodeURIComponent(username), hashed_pw, Date.now(), ip, ip, JSON.stringify(default_settings)];
let sql = `INSERT INTO ipost.users (User_Name, User_PW, User_CreationStamp, User_CreationIP, User_LastIP, User_Settings) VALUES (?, ?, ?, ?, ?, ?);`;
con.query(sql, values, function (err) {
if (err)
throw err;
res.cookie('AUTH_COOKIE', cookiesigned, { maxAge: Math.pow(10, 10), httpOnly: true, secure: DID_I_FINALLY_ADD_HTTPS });
if(req.body.r !== undefined) {
res.redirect(decodeURIComponent(req.body.r))
} else {
res.redirect("/user");
}
});
});
});
router.post("/login", function (req, res) {
if (!increaseAPICall(req, res))
return;
if ((typeof req.body.user) !== "string") {
res.status(416);
res.json({ "error": "incorrect username" });
return;
} }
if (typeof req.body.pass !== 'string') { if ((typeof req.body.pass) !== "string") {
res.status(417) res.status(417);
res.json({ error: 'incorrect password' }) res.json({ "error": "incorrect password" });
return return;
} }
if (!req.body.user) { if (!req.body.user) {
res.status(410) res.status(410);
res.send('no username given') res.send("no username given");
return return;
} }
if (!req.body.pass) { if (!req.body.pass) {
res.status(411) res.status(411);
res.send('no password given') res.send("no password given");
return return;
} }
let username = req.body.user.toString() let username = req.body.user.toString();
username = username.replace(' ', '') username = username.replace(" ", "");
let password = req.body.pass.toString() let password = req.body.pass.toString();
if (!username) { if (!username) {
res.status(412) res.status(412);
res.send('no username given') res.send("no username given");
return return;
} }
if (username.length > 25) { if (username.length > 25) {
res.status(413) res.status(413);
res.send('username is too long') res.send("username is too long");
return return;
} }
if (password.length < 10) { if (password.length < 10) {
res.status(414) res.status(414);
res.send('password is too short') res.send("password is too short");
return return;
} }
if (!password) { if (!password) {
res.status(415) res.status(415);
res.send('no password given') res.send("no password given");
return return;
} }
const no_ip_lock = username.endsWith('@unsafe') const no_ip_lock = username.endsWith("@unsafe")
username = username.replace('@unsafe', '') username = username.replace("@unsafe","")
let less_hashed_pw = SHA256(password, username, HASHES_DIFF) let less_hashed_pw = SHA256(password, username, HASHES_DIFF);
let hashed_pw = SHA256(less_hashed_pw, username, HASHES_COOKIE) let hashed_pw = SHA256(less_hashed_pw, username, HASHES_COOKIE);
let userexistssql = `SELECT * from ipost.users where User_Name = ? and User_PW = ?;` let userexistssql = `SELECT * from ipost.users where User_Name = ? and User_PW = ?;`;
con.query( con.query(userexistssql, [encodeURIComponent(username), hashed_pw], function (_error, result) {
userexistssql, if (result && result[0]) {
[encodeURIComponent(username), hashed_pw], let ip = getIP(req);
function (_error, result) { let setTo = `${username} ${SHA256(password, username, HASHES_COOKIE)}`
if (result && result[0]) { let cookiesigned = signature.sign(setTo, cookiesecret + (!no_ip_lock ? ip : ""));
let ip = getIP(req) res.cookie('AUTH_COOKIE', cookiesigned, { maxAge: Math.pow(10, 10), httpOnly: true, secure: DID_I_FINALLY_ADD_HTTPS });
let setTo = `${username} ${SHA256(password, username, HASHES_COOKIE)}` ip = SHA256(ip, setTo, HASHES_DB);
let cookiesigned = signature.sign( if (result[0].User_LastIP !== ip) {
setTo, let sql = `update ipost.users set User_LastIP = ? where User_Name = ?;`;
cookiesecret + (!no_ip_lock ? ip : '') con.query(sql, [ip, encodeURIComponent(username)], function (error) {
) if (error)
res.cookie('AUTH_COOKIE', cookiesigned, { throw error;
maxAge: Math.pow(10, 10), });
httpOnly: true, }
secure: DID_I_FINALLY_ADD_HTTPS, if(req.body.r !== undefined) {
}) res.redirect(decodeURIComponent(req.body.r))
ip = SHA256(ip, setTo, HASHES_DB)
if (result[0].User_LastIP !== ip) {
let sql = `update ipost.users set User_LastIP = ? where User_Name = ?;`
con.query(
sql,
[ip, encodeURIComponent(username)],
function (error) {
if (error) throw error
}
)
}
if (req.body.r !== undefined) {
res.redirect(decodeURIComponent(req.body.r))
} else {
res.redirect('/user')
}
} else { } else {
console.log(5, 'login failed, username: ', username) res.redirect("/user");
res.redirect('/login?success=false?reason=noUser')
} }
} }
) else {
}) console.log(5,"login failed, username: ", username);
res.redirect("/login?success=false?reason=noUser");
}
});
});
} }

View File

@ -1,16 +1,17 @@
import ejs from 'ejs' import ejs from "ejs"
import { LRUCache as LRU } from 'lru-cache' import { LRUCache as LRU} from "lru-cache"
import { minify as min_js } from 'uglify-js' import {minify as min_js} from "uglify-js"
import Clean from 'clean-css' import Clean from 'clean-css';
import Minifier from 'html-minifier-terser' import Minifier from 'html-minifier-terser';
import { web_version } from 'unsafe_encrypt' import { web_version } from "unsafe_encrypt";
import { existsSync, readFileSync, readFile } from 'fs' import {existsSync, readFileSync, readFile} from "fs"
export const setup = function (router, con, server) { export const setup = function (router, con, server) {
const increaseUSERCall = server.increaseUSERCall
const dir = server.dirname + '/'
ejs.cache = new LRU({ max: 20 }) const increaseUSERCall = server.increaseUSERCall
const dir = server.dirname + "/"
ejs.cache = new LRU({max:20})
const load_var_cache = new LRU({ const load_var_cache = new LRU({
max: 20, max: 20,
@ -21,221 +22,200 @@ export const setup = function (router, con, server) {
ttl: 1000 * 60, ttl: 1000 * 60,
allowStale: true, allowStale: true,
updateAgeOnGet: true, updateAgeOnGet: true,
updateAgeOnHas: true, updateAgeOnHas: true
}) })
function load_var(filePath) { function load_var(filePath) {
if (load_var_cache.has(filePath)) { if (load_var_cache.has(filePath)) {
return load_var_cache.get(filePath) return load_var_cache.get(filePath);
} }
if (!existsSync(filePath)) { if (!existsSync(filePath)) {
console.log(1, 'Tried loading non-existent file', filePath) console.log(1,'Tried loading non-existent file', filePath);
load_var_cache.set(filePath, '') load_var_cache.set(filePath, '');
return '' return '';
} }
let output = readFileSync(filePath) let output = readFileSync(filePath);
if (filePath.endsWith('.js')) { if (filePath.endsWith('.js')) {
output = min_js(output.toString()).code output = min_js(output.toString()).code;
} else if (filePath.endsWith('.css')) { } else if (filePath.endsWith('.css')) {
const { styles } = new Clean({}).minify(output.toString()) const { styles } = new Clean({}).minify(output.toString());
output = styles output = styles;
} }
load_var_cache.set(filePath, output) load_var_cache.set(filePath, output);
return output return output;
} }
function get_channels() { function get_channels(){
return new Promise(function (resolve, reject) { return new Promise(function(resolve, reject) {
let sql = `select post_receiver_name from ipost.posts where post_is_private = '0' group by post_receiver_name;` let sql = `select post_receiver_name from ipost.posts where post_is_private = '0' group by post_receiver_name;`;
con.query(sql, [], function (err, result) { con.query(sql, [], function (err, result) {
if (err) reject(err) if (err)reject(err)
let out = [] let out = []
for (let channel of result) { for(let channel of result){
if (channel.post_receiver_name === '') continue if(channel.post_receiver_name === "")continue;
out[out.length] = channel.post_receiver_name out[out.length] = channel.post_receiver_name
} }
resolve(out) resolve(out)
});
})
}
const appId_Cache = new LRU({max:20,ttl: 1000 * 60 * 15}) //cache for 15 minutes
function getAppWithId(appid) {
appid = Number(appid)
return new Promise((res,rej) => {
if(isNaN(appid)) {
res({})
return
}
if(appId_Cache.has(appid)) {
res(appId_Cache.get(appid) || {})
return
}
con.query("SELECT * FROM ipost.application WHERE application_id=?",[appid],(err,result) => {
if(err) {
console.error(err)
rej({})
return
}
appId_Cache.set(appid,result[0])
res(result[0] || {})
}) })
}) })
} }
const appId_Cache = new LRU({ max: 20, ttl: 1000 * 60 * 15 }) //cache for 15 minutes
function getAppWithId(appid) {
appid = Number(appid)
return new Promise((res, rej) => {
if (isNaN(appid)) {
res({})
return
}
if (appId_Cache.has(appid)) {
res(appId_Cache.get(appid) || {})
return
}
con.query(
'SELECT * FROM ipost.application WHERE application_id=?',
[appid],
(err, result) => {
if (err) {
console.error(err)
rej({})
return
}
appId_Cache.set(appid, result[0])
res(result[0] || {})
}
)
})
}
let global_page_variables = { let global_page_variables = {
globalcss: load_var('./css/global.css'), globalcss: load_var("./css/global.css"),
httppostjs: load_var('./js/httppost.js'), httppostjs: load_var("./js/httppost.js"),
navbar: load_var('./extra_modules/navbar.html'), navbar: load_var("./extra_modules/navbar.html"),
markdownjs: load_var('./js/markdown.js'), markdownjs: load_var("./js/markdown.js"),
htmlescapejs: load_var('./js/htmlescape.js'), htmlescapejs: load_var("./js/htmlescape.js"),
warnmessagejs: load_var('./js/warn_message.js'), warnmessagejs: load_var("./js/warn_message.js"),
loadfile: load_var, loadfile: load_var,
getChannels: get_channels, getChannels: get_channels,
encryptJS: min_js(web_version().toString()).code, encryptJS: min_js(web_version().toString()).code,
cookiebanner: `<script id="cookieyes" type="text/javascript" src="https://cdn-cookieyes.com/client_data/3cf33f6b631f3587bf83813b/script.js" async></script>`, cookiebanner: `<script id="cookieyes" type="text/javascript" src="https://cdn-cookieyes.com/client_data/3cf33f6b631f3587bf83813b/script.js" async></script>`,
newrelic: load_var("./extra_modules/newrelic_monitor.html"),
getPID: server.global_page_variables.getPID, getPID: server.global_page_variables.getPID,
getDMPID: server.global_page_variables.getDMPID, getDMPID: server.global_page_variables.getDMPID,
unauthorized_description: 'Chat now by creating an account on IPost', unauthorized_description: "Chat now by creating an account on IPost",
hcaptcha_sitekey: server.hcaptcha.sitekey, hcaptcha_sitekey: server.hcaptcha.sitekey,
getAppWithId: getAppWithId, getAppWithId: getAppWithId
} }
async function handleUserFiles(request, response, overrideurl) { async function handleUserFiles(request, response, overrideurl) {
if (!increaseUSERCall(request, response)) return if (!increaseUSERCall(request, response))return;
if (typeof overrideurl !== 'string') overrideurl = undefined if(typeof overrideurl !== "string")overrideurl = undefined;
let originalUrl = let originalUrl = overrideurl
overrideurl || || request.params.file
request.params.file || || request.originalUrl.split("?").shift(); //backup in case anything goes wrong
request.originalUrl.split('?').shift() //backup in case anything goes wrong
let path = '' let path = ""
if (existsSync(dir + 'views/' + originalUrl)) { if (existsSync(dir + "views/" + originalUrl)) {
path = dir + 'views/' + originalUrl path = dir + "views/" + originalUrl
//send .txt files as plaintext to help browsers interpret it correctly //send .txt files as plaintext to help browsers interpret it correctly
if (originalUrl.endsWith('.txt')) { if(originalUrl.endsWith(".txt")) {
response.set('Content-Type', 'text/plain') response.set('Content-Type', 'text/plain');
readFile(path, (err, data) => { readFile(path,(err,data)=> {
if (err) return if(err)return
response.send(data) response.send(data)
}) })
return return
} }
} }
if (existsSync(dir + 'views/' + originalUrl + 'index.html')) { if (existsSync(dir + "views/" + originalUrl + "index.html")) {
path = dir + 'views/' + originalUrl + 'index.html' path = dir + "views/" + originalUrl + "index.html"
} }
if (existsSync(dir + 'views/' + originalUrl + '.html')) { if (existsSync(dir + "views/" + originalUrl + ".html")) {
path = dir + 'views/' + originalUrl + '.html' path = dir + "views/" + originalUrl + ".html"
} }
if (existsSync(dir + 'views' + originalUrl + '.html')) { if (existsSync(dir + "views" + originalUrl + ".html")) {
path = dir + 'views' + originalUrl + '.html' path = dir + "views" + originalUrl + ".html"
} }
if ( if(path !== "" && originalUrl !== "favicon.ico" && originalUrl !== "api_documentation" && originalUrl !== "api_documentation.html") {
path !== '' &&
originalUrl !== 'favicon.ico' &&
originalUrl !== 'api_documentation' &&
originalUrl !== 'api_documentation.html'
) {
console.log(originalUrl) console.log(originalUrl)
global_page_variables.user = { global_page_variables.user = { "username": response.locals.username, "bio": response.locals.bio, "avatar": response.locals.avatar }
username: response.locals.username,
bio: response.locals.bio,
avatar: response.locals.avatar,
}
global_page_variables.query = request.query global_page_variables.query = request.query
if (originalUrl === 'authorize') { if(originalUrl === "authorize") {
global_page_variables.application = await getAppWithId( global_page_variables.application = await getAppWithId(request.query.id)
request.query.id
)
} }
ejs.renderFile( ejs.renderFile(path,global_page_variables,{async: true},async function(err,str){
path, str = await str
global_page_variables, err = await err
{ async: true }, if(err) {
async function (err, str) { console.log(1,err)
str = await str response.status(500)
err = await err response.send("error")
if (err) { //TODO: make error page
console.log(1, err) return
response.status(500)
response.send('error')
//TODO: make error page
return
}
try {
str = await Minifier.minify(str, {
removeComments: true,
removeCommentsFromCDATA: true,
removeCDATASectionsFromCDATA: true,
collapseWhitespace: true,
collapseBooleanAttributes: true,
removeAttributeQuotes: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
})
} catch (ignored) {
console.log(2, 'error minifying', originalUrl)
}
try {
response.send(str)
} catch (err) {
console.error(err)
}
} }
) try {
return str = await Minifier.minify(str,{
removeComments: true,
removeCommentsFromCDATA: true,
removeCDATASectionsFromCDATA: true,
collapseWhitespace: true,
collapseBooleanAttributes: true,
removeAttributeQuotes: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true
})
} catch(ignored){
console.log(2,"error minifying",originalUrl);
}
try {
response.send(str)
} catch(err) {
console.error(err)
}
})
return;
} }
if ( if(originalUrl === "api_documentation" || originalUrl === "api_documentation.html") {
originalUrl === 'api_documentation' || response.set('Cache-Control', 'public, max-age=2592000');
originalUrl === 'api_documentation.html'
) {
response.set('Cache-Control', 'public, max-age=2592000')
response.set('Content-Type', 'text/html') response.set('Content-Type', 'text/html')
response.send(load_var('./views/api_documentation.html')) response.send(load_var("./views/api_documentation.html"))
return return
} }
if (originalUrl === 'favicon.ico') { if(originalUrl === "favicon.ico") {
response.set('Cache-Control', 'public, max-age=2592000') response.set('Cache-Control', 'public, max-age=2592000');
response.sendFile(dir + '/views/favicon.ico') response.sendFile(dir + "/views/favicon.ico")
return return
} }
console.log(5, 'no file found', originalUrl) console.log(5,"no file found",originalUrl);
try { try {
response.status(404).send('No file with that name found') response.status(404).send("No file with that name found");
} catch (err) { } catch(err) {
console.error(err) console.error(err)
} }
} }
/** /**
* Handle default URI as /index (interpreted redirect: "localhost" -> "localhost/index" ) * Handle default URI as /index (interpreted redirect: "localhost" -> "localhost/index" )
*/ */
router.get('/', (req, res) => { router.get("/", (req, res) => {
req.params.file = 'index' req.params.file = "index"
handleUserFiles(req, res, '/index') handleUserFiles(req,res,"/index")
}) });
router.get('/:file', handleUserFiles) router.get("/:file", handleUserFiles);
router.get('/:folder/:file', (req, res) => { router.get("/:folder/:file", (req, res) => {
req.params.file = req.params.folder + '/' + req.params.file req.params.file = req.params.folder+"/"+req.params.file
handleUserFiles(req, res) handleUserFiles(req,res)
}) });
} }

486
server.js
View File

@ -1,21 +1,24 @@
import http from 'http' import "newrelic"
import express, { Router } from 'express'
import useragent from 'express-useragent'
import fileUpload from 'express-fileupload'
import * as bodyParser from 'body-parser'
import cookieParser from 'cookie-parser'
import * as mysql from 'mysql2'
import * as ws from 'ws'
import getIP from './extra_modules/getip.js'
import { unsign } from './extra_modules/unsign.js'
import { readFileSync, appendFile } from 'fs'
import { format } from 'util'
import { setup as SETUP_ROUTES } from './routes/setup_all_routes.js'
import { verify as verifyHCaptcha_int } from 'hcaptcha'
import { ensureExists } from './extra_modules/ensureExists.js' import http from "http";
import express,{Router} from "express";
import useragent from "express-useragent";
import fileUpload from "express-fileupload";
import * as bodyParser from "body-parser";
import cookieParser from "cookie-parser";
import * as mysql from "mysql2";
import * as ws from "ws";
import getIP from "./extra_modules/getip.js";
import {unsign} from "./extra_modules/unsign.js";
import { readFileSync, appendFile } from "fs";
import { format } from "util";
import { setup as SETUP_ROUTES} from "./routes/setup_all_routes.js"
import { verify as verifyHCaptcha_int } from "hcaptcha"
import hsts from "hsts"
import * as compress from 'compression' import { ensureExists } from "./extra_modules/ensureExists.js"
import * as compress from "compression"
const compression = compress.default const compression = compress.default
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
@ -23,9 +26,9 @@ import { dirname } from 'path'
const __filename = fileURLToPath(import.meta.url) const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename) const __dirname = dirname(__filename)
const config = JSON.parse(readFileSync('server_config.json')) const config = JSON.parse(readFileSync("server_config.json"));
const time = Date.now() const time = Date.now();
const original_log = console.log const original_log = console.log;
/** /**
* custom logging function * custom logging function
* @param {number} level importance level if information * @param {number} level importance level if information
@ -33,48 +36,41 @@ const original_log = console.log
* @return {undefined} returns nothing * @return {undefined} returns nothing
*/ */
function log_info(level, ...info) { function log_info(level, ...info) {
let text = info let text = info;
if (text === undefined || text.length === 0) { if (text === undefined || text.length === 0) {
text = level text = level;
level = 5 level = 5;
} }
if ( if (config["logs"] && config["logs"]["level"] && config["logs"]["level"] >= level) {
config['logs'] && let tolog = `[INFO] [${Date.now()}] : ${format(text)} \n`;
config['logs']['level'] && original_log(tolog); //still has some nicer colors
config['logs']['level'] >= level
) {
let tolog = `[INFO] [${Date.now()}] : ${format(text)} \n`
original_log(tolog) //still has some nicer colors
ensureExists(__dirname + '/logs/', function (err) { ensureExists(__dirname + '/logs/', function (err) {
if (err) { if (err) {
process.stderr.write(tolog) //just write it to stderr process.stderr.write(tolog); //just write it to stderr
} else {
appendFile(__dirname + '/logs/' + time, tolog, function (err) {
if (err) {
process.stderr.write(err)
}
})
} }
}) else {
appendFile(__dirname + "/logs/" + time, tolog, function (err) {
if (err) {
process.stderr.write(err);
}
});
}
});
} }
} }
//console.log = log_info; console.log = log_info;
const hcaptcha_secret = config.hcaptcha_secret const hcaptcha_secret = config.hcaptcha_secret
// wrapper for the HCaptcha verify function // wrapper for the HCaptcha verify function
function verifyHCaptcha(token) { function verifyHCaptcha(token) {
return verifyHCaptcha_int( return verifyHCaptcha_int(hcaptcha_secret,token,undefined,config.hcaptcha_sitekey)
hcaptcha_secret,
token,
undefined,
config.hcaptcha_sitekey
)
} }
const WebSocket = ws.WebSocketServer const WebSocket = ws.WebSocketServer;
const router = Router() const router = Router();
const app = express() const app = express();
const con = mysql.createPool({ const con = mysql.createPool({
connectionLimit: config.mysql.connections, connectionLimit: config.mysql.connections,
host: config.mysql.host, host: config.mysql.host,
@ -82,8 +78,8 @@ const con = mysql.createPool({
password: readFileSync(config.mysql.password_file).toString(), password: readFileSync(config.mysql.password_file).toString(),
multipleStatements: true, multipleStatements: true,
supportBigNumbers: true, supportBigNumbers: true,
}) });
const cookiesecret = readFileSync('cookiesecret.txt').toString() const cookiesecret = readFileSync("cookiesecret.txt").toString();
/** /**
* custom, bad random number generator * custom, bad random number generator
@ -92,203 +88,204 @@ const cookiesecret = readFileSync('cookiesecret.txt').toString()
*/ */
class RNG { class RNG {
constructor(seed) { constructor(seed) {
if (!seed) seed = Date.now() if (!seed)
this.seed = seed seed = Date.now();
this.seed = seed;
this.random = function (min, max) { this.random = function (min, max) {
if (!min) min = 0 if (!min)
min = 0;
if (!max) { if (!max) {
max = min max = min;
min = 0 min = 0;
} }
this.seed += Math.log(Math.abs(Math.sin(this.seed)) * 100) this.seed += Math.log(Math.abs(Math.sin(this.seed)) * 100);
return Math.abs(Math.sin(this.seed)) * max + min return Math.abs(Math.sin(this.seed)) * max + min;
} };
this.rand = function (min, max) { this.rand = function (min, max) {
return Math.floor(this.random(min, max)) return Math.floor(this.random(min, max));
} };
} }
} }
const rand = new RNG() const rand = new RNG();
const genstring_characters = const genstring_characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' const genstring_charactersLength = genstring_characters.length;
const genstring_charactersLength = genstring_characters.length
/** /**
* generates a semi-random string * generates a semi-random string
* @param {number} length length of string to generate * @param {number} length length of string to generate
* @return {string} semi-random string generated * @return {string} semi-random string generated
*/ */
function genstring(length) { function genstring(length) {
let result = '' let result = "";
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
result += genstring_characters.charAt( result += genstring_characters.charAt(rand.rand(genstring_charactersLength));
rand.rand(genstring_charactersLength)
)
} }
return result return result;
} }
var API_CALLS = {} var API_CALLS = {};
var API_CALLS_ACCOUNT = {} var API_CALLS_ACCOUNT = {};
var USER_CALLS = {} var USER_CALLS = {};
var SESSIONS = {} var SESSIONS = {};
var REVERSE_SESSIONS = {} var REVERSE_SESSIONS = {};
var INDIVIDUAL_CALLS = {} var INDIVIDUAL_CALLS = {};
/** /**
* clears current api call list (per IP) * clears current api call list (per IP)
* @return {undefined} returns nothing * @return {undefined} returns nothing
*/ */
function clear_api_calls() { function clear_api_calls() {
API_CALLS = {} API_CALLS = {};
} }
/** /**
* clears current api account call list (per account) * clears current api account call list (per account)
* @return {undefined} returns nothing * @return {undefined} returns nothing
*/ */
function clear_account_api_calls() { function clear_account_api_calls() {
API_CALLS_ACCOUNT = {} API_CALLS_ACCOUNT = {};
} }
/** /**
* clears current user file call list (per IP) * clears current user file call list (per IP)
* @return {undefined} returns nothing * @return {undefined} returns nothing
*/ */
function clear_user_calls() { function clear_user_calls() {
USER_CALLS = {} USER_CALLS = {};
} }
setInterval(clear_api_calls, config.rate_limits.api.reset_time) setInterval(clear_api_calls, config.rate_limits.api.reset_time);
setInterval(clear_account_api_calls, config.rate_limits.api.reset_time) setInterval(clear_account_api_calls, config.rate_limits.api.reset_time);
setInterval(clear_user_calls, config.rate_limits.user.reset_time) setInterval(clear_user_calls, config.rate_limits.user.reset_time);
function increaseIndividualCall(url, req) { function increaseIndividualCall(url, req) {
let conf = config['rate_limits']['individual'][url] let conf = config["rate_limits"]["individual"][url];
if (!conf) { if (!conf) {
//if(!url.startsWith("/avatars/")) //ignore avatars /* DEBUG: inidividual ratelimiters */ //if(!url.startsWith("/avatars/")) //ignore avatars /* DEBUG: inidividual ratelimiters */
//console.log(5, "url not in individual ratelimiter", url); /* DEBUG: inidividual ratelimiters */ //console.log(5, "url not in individual ratelimiter", url); /* DEBUG: inidividual ratelimiters */
return true return true;
} }
if (!conf['enabled']) return true if (!conf["enabled"])
let ip = getIP(req) return true;
if (INDIVIDUAL_CALLS[ip] === undefined) INDIVIDUAL_CALLS[ip] = {} let ip = getIP(req);
if (INDIVIDUAL_CALLS[ip][url] === undefined) INDIVIDUAL_CALLS[ip][url] = 0 if (INDIVIDUAL_CALLS[ip] === undefined)
INDIVIDUAL_CALLS[ip] = {};
if (INDIVIDUAL_CALLS[ip][url] === undefined)
INDIVIDUAL_CALLS[ip][url] = 0;
if (INDIVIDUAL_CALLS[ip][url] === 0) { if (INDIVIDUAL_CALLS[ip][url] === 0) {
setTimeout(function () { setTimeout(function () {
INDIVIDUAL_CALLS[ip][url] = 0 INDIVIDUAL_CALLS[ip][url] = 0;
}, conf['reset_time']) }, conf["reset_time"]);
} }
INDIVIDUAL_CALLS[ip][url]++ INDIVIDUAL_CALLS[ip][url]++;
if (INDIVIDUAL_CALLS[ip][url] >= conf['max']) { if (INDIVIDUAL_CALLS[ip][url] >= conf["max"]) {
console.log( console.log(3, "ratelimiting someone on", url, INDIVIDUAL_CALLS[ip][url], conf["max"],ip);
3, return false;
'ratelimiting someone on',
url,
INDIVIDUAL_CALLS[ip][url],
conf['max'],
ip
)
return false
} }
return true return true;
} }
function increaseAccountAPICall(req, res) { function increaseAccountAPICall(req, res) {
let cookie = req.cookies.AUTH_COOKIE let cookie = req.cookies.AUTH_COOKIE;
if (!cookie) { if (!cookie) {
return true return true;
} }
let unsigned = unsign(cookie, req, res) let unsigned = unsign(cookie, req, res);
if (!unsigned) { if (!unsigned) {
return true //if there's no account, why not just ignore it return true; //if there's no account, why not just ignore it
} }
unsigned = decodeURIComponent(unsigned) unsigned = decodeURIComponent(unsigned);
if (!unsigned) return false if (!unsigned)
let values = unsigned.split(' ') return false;
let username = values[0] let values = unsigned.split(" ");
let username = values[0];
if (API_CALLS_ACCOUNT[username] === undefined) if (API_CALLS_ACCOUNT[username] === undefined)
API_CALLS_ACCOUNT[username] = 0 API_CALLS_ACCOUNT[username] = 0;
if (API_CALLS_ACCOUNT[username] >= config.rate_limits.api.max_per_account) { if (API_CALLS_ACCOUNT[username] >= config.rate_limits.api.max_per_account) {
res.status(429) res.status(429);
res.send('You are sending way too many api calls!') res.send("You are sending way too many api calls!");
return false return false;
} }
return true return true;
} }
function increaseAPICall(req, res, next) { function increaseAPICall(req, res, next) {
let ip = getIP(req) let ip = getIP(req);
if (API_CALLS[ip] === undefined) API_CALLS[ip] = 0 if (API_CALLS[ip] === undefined)
API_CALLS[ip] = 0;
if (API_CALLS[ip] >= config.rate_limits.api.max_without_session) { if (API_CALLS[ip] >= config.rate_limits.api.max_without_session) {
if ( if (REVERSE_SESSIONS[ip] && req.cookies.session !== REVERSE_SESSIONS[ip]) { //expected a session, but didn't get one
REVERSE_SESSIONS[ip] && res.status(429);
req.cookies.session !== REVERSE_SESSIONS[ip] res.send("You are sending way too many api calls!");
) { return;
//expected a session, but didn't get one
res.status(429)
res.send('You are sending way too many api calls!')
return
} }
if (!req.cookies.session) { if (!req.cookies.session) {
let session let session;
do { do {
session = genstring(300) session = genstring(300);
} while (SESSIONS[session] !== undefined) } while (SESSIONS[session] !== undefined);
SESSIONS[session] = ip SESSIONS[session] = ip;
REVERSE_SESSIONS[ip] = session REVERSE_SESSIONS[ip] = session;
setTimeout(function () { setTimeout(function () {
SESSIONS[session] = undefined SESSIONS[session] = undefined;
REVERSE_SESSIONS[ip] = undefined REVERSE_SESSIONS[ip] = undefined;
}, 50000) }, 50000);
res.cookie('session', session, { res.cookie('session', session, { maxAge: 100000, httpOnly: true, secure: true });
maxAge: 100000, console.log(3, "sending session to " + ip);
httpOnly: true,
secure: true,
})
console.log(3, 'sending session to ' + ip)
} }
} }
if (API_CALLS[ip] >= config.rate_limits.api.max_with_session) { if (API_CALLS[ip] >= config.rate_limits.api.max_with_session) {
res.status(429) res.status(429);
res.send('You are sending too many api calls!') res.send("You are sending too many api calls!");
console.log(3, 'rate limiting ' + ip) console.log(3, "rate limiting " + ip);
return false return false;
} }
API_CALLS[ip]++ API_CALLS[ip]++;
if (!increaseAccountAPICall(req, res)) return false //can't forget account-based ratelimits if (!increaseAccountAPICall(req, res))
if (next) next() return false; //can't forget account-based ratelimits
return true if (next)
next();
return true;
} }
function increaseUSERCall(req, res, next) { function increaseUSERCall(req, res, next) {
let ip = getIP(req) let ip = getIP(req);
if (USER_CALLS[ip] === undefined) USER_CALLS[ip] = 0 if (USER_CALLS[ip] === undefined)
USER_CALLS[ip] = 0;
if (USER_CALLS[ip] >= config.rate_limits.user.max) { if (USER_CALLS[ip] >= config.rate_limits.user.max) {
res.status(429) res.status(429);
res.send('You are sending too many requests!') res.send("You are sending too many requests!");
console.log(2, 'rate limiting ' + ip) console.log(2, "rate limiting " + ip);
return false return false;
} }
USER_CALLS[ip]++ USER_CALLS[ip]++;
if (next) next() if (next)
return true next();
return true;
} }
console.log(5, 'loading routes') console.log(5, "loading routes");
app.use(useragent.express()) app.use(useragent.express());
app.use( app.use(fileUpload({
fileUpload({ limits: {
limits: { files: 5,
files: 5, fileSize: 1_000_000
fileSize: 1_000_000, }
}, }));
})
)
app.use((_req, res, next) => { const hstsMiddleware = hsts({
res.set('x-powered-by', 'ipost') maxAge: 31536000,
res.set('X-Frame-Options', 'DENY') includeSubDomains: true,
res.set('X-XSS-Protection', '1; mode=block') preload: true
res.set('X-Content-Type-Options', 'nosniff')
res.set('Referrer-Policy', 'no-referrer')
next()
}) })
app.use(bodyParser.default.json({ limit: '100mb' })) app.use((req, res, next) => {
app.use(bodyParser.default.urlencoded({ limit: '100mb', extended: true })) res.set("x-powered-by", "ipost");
app.use(cookieParser(cookiesecret)) res.set("X-Frame-Options","DENY");
res.set("X-XSS-Protection","1; mode=block");
res.set("X-Content-Type-Options","nosniff");
res.set("Referrer-Policy","no-referrer");
if (req.secure) {
hstsMiddleware(req, res, next)
} else {
next()
}
})
app.use(bodyParser.default.json({ limit: "100mb" }));
app.use(bodyParser.default.urlencoded({ limit: "100mb", extended: true }));
app.use(cookieParser(cookiesecret));
app.use(compression()) app.use(compression())
let blocked_headers = [ let blocked_headers = [
'HTTP_VIA', 'HTTP_VIA',
@ -305,42 +302,55 @@ let blocked_headers = [
'FORWARDED', 'FORWARDED',
'CLIENT_IP', 'CLIENT_IP',
'FORWARDED_FOR_IP', 'FORWARDED_FOR_IP',
'HTTP_PROXY_CONNECTION', 'HTTP_PROXY_CONNECTION'
] ];
if (!config.disallow_proxies_by_headers) { if (!config.disallow_proxies_by_headers) {
blocked_headers = [] blocked_headers = [];
} }
app.use(function (_req, res, next) { app.use(function (_req, res, next) {
res.set('X-XSS-Protection', '1; mode=block') res.set("X-XSS-Protection", "1; mode=block");
next() next();
}) });
app.use('/*path', function (req, res, next) { //auto redirect to https
app.use((req, res, next) => {
if (req.secure) {
//already secure
next();
}
else {
//redirect to https
res.redirect('https://' + req.headers.host + req.url);
}
});
app.use("/*", function (req, res, next) {
for (let i = 0; i < blocked_headers.length; i++) { for (let i = 0; i < blocked_headers.length; i++) {
if (req.header(blocked_headers[i]) !== undefined) { if (req.header(blocked_headers[i]) !== undefined) {
res.json({ error: "we don't allow proxies on our website." }) res.json({ "error": "we don't allow proxies on our website." });
return return;
} }
} }
let fullurl = req.baseUrl + req.path let fullurl = req.baseUrl + req.path;
if (fullurl !== '/') { if (fullurl !== "/") {
fullurl = fullurl.substring(0, fullurl.length - 1) fullurl = fullurl.substring(0, fullurl.length - 1);
} }
if (!increaseIndividualCall(fullurl, req)) { if (!increaseIndividualCall(fullurl, req)) {
res.status(429) res.status(429);
res.json({ error: 'you are sending too many requests!' }) res.json({ "error": "you are sending too many requests!" });
return return;
} }
next() next();
}) });
console.log(5, 'finished loading user routes, starting with api routes') console.log(5, "finished loading user routes, starting with api routes");
/* /*
START /API/* START /API/*
*/ */
var wss var wss;
var commonfunctions = { var commonfunctions = {
increaseAPICall, increaseAPICall,
increaseUSERCall, increaseUSERCall,
@ -349,67 +359,81 @@ var commonfunctions = {
wss, wss,
genstring, genstring,
ensureExists, ensureExists,
dirname: __dirname, "dirname": __dirname,
config, config,
hcaptcha: { hcaptcha: {
verify: verifyHCaptcha, "verify":verifyHCaptcha,
sitekey: config.hcaptcha_sitekey, "sitekey":config.hcaptcha_sitekey
}, }
} };
SETUP_ROUTES(router, con, commonfunctions) SETUP_ROUTES(router,con,commonfunctions)
router.get('/api/getChannels', function (_req, res) {
res.set('Access-Control-Allow-Origin', '*') router.get("/api/getChannels", function (_req, res) {
let sql = `select post_receiver_name from ipost.posts where post_is_private = '0' group by post_receiver_name;` res.set("Access-Control-Allow-Origin", "*");
let sql = `select post_receiver_name from ipost.posts where post_is_private = '0' group by post_receiver_name;`;
con.query(sql, [], function (err, result) { con.query(sql, [], function (err, result) {
if (err) throw err if (err)
res.json(result) throw err;
}) res.json(result);
});
/* #swagger.security = [{ /* #swagger.security = [{
"appTokenAuthHeader": [] "appTokenAuthHeader": []
}] */ }] */
}) });
/* /*
END /API/* END /API/*
*/ */
console.log(5, 'finished loading routes') console.log(5, "finished loading routes");
app.use(router) app.use(router);
const httpServer = http.createServer(app) const httpServer = http.createServer(app);
httpServer.listen(config['ports']['http'], function () { httpServer.listen(config["ports"]["http"], function () {
console.log(5, 'HTTP Server is listening') console.log(5, "HTTP Server is listening");
}) });
const privateKey = readFileSync(config["ssl"]["privateKey"]).toString();
const certificate = readFileSync(config["ssl"]["certificate"]).toString();
const credentials = { key: privateKey, cert: certificate };
var httpsServer;
import spdy from "spdy"
httpsServer = spdy.createServer(credentials,app)
//httpsServer = https.createServer(credentials, app);
httpsServer.listen(config["ports"]["https"], function () {
console.log(5, "HTTPS Server is listening");
});
wss = new WebSocket({ wss = new WebSocket({
server: httpServer, server: httpsServer,
perMessageDeflate: { perMessageDeflate: {
zlibDeflateOptions: { zlibDeflateOptions: {
chunkSize: 1024, chunkSize: 1024,
memLevel: 7, memLevel: 7,
level: 3, level: 3
}, },
zlibInflateOptions: { zlibInflateOptions: {
chunkSize: 10 * 1024, chunkSize: 10 * 1024
}, },
clientNoContextTakeover: true, clientNoContextTakeover: true,
serverNoContextTakeover: true, serverNoContextTakeover: true,
serverMaxWindowBits: 10, serverMaxWindowBits: 10,
concurrencyLimit: 10, concurrencyLimit: 10,
threshold: 1024 * 16, threshold: 1024 * 16
}, }
}) });
wss.on('connection', function connection(ws) { wss.on("connection", function connection(ws) {
ws.channel = 'everyone' ws.channel = "everyone";
console.log(5, 'new connection') console.log(5,"new connection");
ws.on('message', function incoming(message) { ws.on("message", function incoming(message) {
message = JSON.parse(message) message = JSON.parse(message);
if (message.id === 'switchChannel') { if (message.id === "switchChannel") {
ws.channel = decodeURIComponent(message.data) ws.channel = decodeURIComponent(message.data);
} }
}) });
}) });
commonfunctions.wss = wss commonfunctions.wss = wss;
console.log(5, 'starting up all services') console.log(5, "starting up all services");

View File

@ -1,168 +1,168 @@
{ {
"allow_getotheruser_without_cookie": true, "allow_getotheruser_without_cookie": true,
"preferred_ip_header": "x-real-ip", "preferred_ip_header": "x-real-ip",
"only_prefer_when_ip": "::ffff:127.0.0.1", "only_prefer_when_ip": "::ffff:127.0.0.1",
"mysql": { "mysql": {
"connections": 1000, "connections":1000,
"host": "db", "host":"localhost",
"user": "ipost", "user":"root",
"password_file": "mysql_password.txt" "password_file":"mysql_password.txt"
},
"cookies": {
"server_hashes": 10000,
"client_hashes": 10
},
"rate_limits": {
"api": {
"reset_time": 40000,
"max_without_session": 30,
"max_with_session": 120,
"max_per_account": 200
}, },
"cookies": { "user": {
"server_hashes": 10000, "reset_time": 30000,
"client_hashes": 10 "max": 60
}, },
"rate_limits": { "individual": {
"api": { "/" : {
"reset_time": 40000, "enabled": true,
"max_without_session": 30, "max": 4,
"max_with_session": 120, "reset_time": 10000
"max_per_account": 200 },
}, "/favicon.ico": {
"user": { "enabled": true,
"reset_time": 30000, "max": 5,
"max": 60 "reset_time": 5000
}, },
"individual": { "/js/warn_message.js" : {
"/": { "enabled": true,
"enabled": true, "max": 10,
"max": 4, "reset_time": 5000
"reset_time": 10000 },
}, "/js/addnavbar.js" : {
"/favicon.ico": { "enabled": true,
"enabled": true, "max": 10,
"max": 5, "reset_time": 5000
"reset_time": 5000 },
}, "/css/style.css" : {
"/js/warn_message.js": { "enabled": true,
"enabled": true, "max": 5,
"max": 10, "reset_time": 5000
"reset_time": 5000 },
}, "/css/logon.css" : {
"/js/addnavbar.js": { "enabled": true,
"enabled": true, "max": 10,
"max": 10, "reset_time": 5000
"reset_time": 5000 },
}, "/css/global.css" : {
"/css/style.css": { "enabled": true,
"enabled": true, "max": 10,
"max": 5, "reset_time": 5000
"reset_time": 5000 },
}, "/api/getuser" : {
"/css/logon.css": { "enabled": true,
"enabled": true, "max": 10,
"max": 10, "reset_time": 10000
"reset_time": 5000 },
}, "/api/getotheruser" : {
"/css/global.css": { "enabled": true,
"enabled": true, "max": 60,
"max": 10, "reset_time": 10000
"reset_time": 5000 },
}, "/login" : {
"/api/getuser": { "enabled": true,
"enabled": true, "max": 6,
"max": 10, "reset_time": 10000
"reset_time": 10000 },
}, "/settings" : {
"/api/getotheruser": { "enabled": true,
"enabled": true, "max": 4,
"max": 60, "reset_time": 5000
"reset_time": 10000 },
}, "/images/default_avatar.png" : {
"/login": { "enabled": true,
"enabled": true, "max": 20,
"max": 6, "reset_time": 10000
"reset_time": 10000 },
}, "/images/bot.png" : {
"/settings": { "enabled": true,
"enabled": true, "max": 10,
"max": 4, "reset_time": 10000
"reset_time": 5000 },
}, "/images/settings_min.png" : {
"/images/default_avatar.png": { "enabled": true,
"enabled": true, "max": 10,
"max": 20, "reset_time": 10000
"reset_time": 10000 },
}, "/js/markdown.js" : {
"/images/bot.png": { "enabled": true,
"enabled": true, "max": 5,
"max": 10, "reset_time": 10000
"reset_time": 10000 },
}, "/posts" : {
"/images/settings_min.png": { "enabled": true,
"enabled": true, "max": 5,
"max": 10, "reset_time": 10000
"reset_time": 10000 },
}, "/js/httppost.js" : {
"/js/markdown.js": { "enabled": true,
"enabled": true, "max": 10,
"max": 5, "reset_time": 10000
"reset_time": 10000 },
}, "/js/htmlescape.js" : {
"/posts": { "enabled": true,
"enabled": true, "max": 10,
"max": 5, "reset_time": 10000
"reset_time": 10000 },
}, "/api/getPosts" : {
"/js/httppost.js": { "enabled": true,
"enabled": true, "max": 10,
"max": 10, "reset_time": 20000
"reset_time": 10000 },
}, "/api/setBio": {
"/js/htmlescape.js": { "enabled": true,
"enabled": true, "max": 3,
"max": 10, "reset_time": 20000
"reset_time": 10000 },
}, "/api/setavatar": {
"/api/getPosts": { "enabled": true,
"enabled": true, "max": 6,
"max": 10, "reset_time": 120000
"reset_time": 20000 },
}, "/api/getPost": {
"/api/setBio": { "enabled": true,
"enabled": true, "max": 40,
"max": 3, "reset_time": 30000
"reset_time": 20000 },
}, "/api/pid": {
"/api/setavatar": { "enabled": true,
"enabled": true, "max": 30,
"max": 6, "reset_time": 30000
"reset_time": 120000 },
}, "api/getChannels": {
"/api/getPost": { "enabled": true,
"enabled": true, "max": 10,
"max": 40, "reset_time": 20000
"reset_time": 30000 },
}, "api/getPostsLowerThan": {
"/api/pid": { "enabled": true,
"enabled": true, "max": 20,
"max": 30, "reset_time": 10000
"reset_time": 30000 }
}, }
"api/getChannels": { },
"enabled": true, "logs": {
"max": 10, "level": 5
"reset_time": 20000 },
}, "ssl": {
"api/getPostsLowerThan": { "privateKey": "/etc/letsencrypt/live/ipost.rocks-0002/privkey.pem",
"enabled": true, "certificate" : "/etc/letsencrypt/live/ipost.rocks-0002/fullchain.pem"
"max": 20, },
"reset_time": 10000 "ports": {
} "http": 9999,
} "https": 9998
}, },
"logs": { "disallow_proxies_by_headers": true,
"level": 5 "hcaptcha_secret": "0x0000000000000000000000000000000000000000",
}, "hcaptcha_sitekey": "10000000-ffff-ffff-ffff-000000000001"
"ssl": {
"privateKey": "/etc/letsencrypt/live/ipost.rocks-0002/privkey.pem",
"certificate": "/etc/letsencrypt/live/ipost.rocks-0002/fullchain.pem"
},
"ports": {
"http": 80,
"https": 443
},
"disallow_proxies_by_headers": true,
"hcaptcha_secret": "0x0000000000000000000000000000000000000000",
"hcaptcha_sitekey": "10000000-ffff-ffff-ffff-000000000001"
} }

View File

@ -1,79 +1,78 @@
const fs = require('fs') const fs = require('fs');
const swaggerAutogen = require('swagger-autogen')() const swaggerAutogen = require('swagger-autogen')();
const doc = { const doc = {
info: { info: {
title: 'IPost API', title: 'IPost API',
description: 'the official IPost.rocks API documentation', description: 'the official IPost.rocks API documentation',
}, },
host: 'ipost.rocks', host: 'ipost.rocks',
schemes: ['https'], schemes: ['https'],
securityDefinitions: { securityDefinitions: {
appTokenAuthHeader: { appTokenAuthHeader: {
type: 'apiKey', type: 'apiKey',
in: 'header', // can be 'header', 'query' or 'cookie' in: 'header', // can be 'header', 'query' or 'cookie'
name: 'ipost-auth-token', // name of the header, query parameter or cookie name: 'ipost-auth-token', // name of the header, query parameter or cookie
description: description: 'authenticate using the authentication object in the header'
'authenticate using the authentication object in the header', }
}, }
}, };
}
const outputFile = './swagger-api.json' const outputFile = './swagger-api.json';
const tempFile = './swagger-output.json' const tempFile = './swagger-output.json';
const endpointsFiles = ['./server.js'] const endpointsFiles = ['./server.js'];
function pushdirectory(currentpath) { function pushdirectory(currentpath) {
fs.readdirSync(currentpath, { fs.readdirSync(currentpath, {
withFileTypes: true, withFileTypes: true
}).forEach((dirent) => { }).forEach(dirent => {
if (dirent.isFile()) { if (dirent.isFile()) {
endpointsFiles.push(currentpath + dirent.name) endpointsFiles.push(currentpath + dirent.name);
} else { } else {
pushdirectory(currentpath + dirent.name + '/') pushdirectory(currentpath + dirent.name + "/");
} }
}) });
} }
pushdirectory('./routes/') pushdirectory("./routes/");
swaggerAutogen(tempFile, endpointsFiles, doc) swaggerAutogen(tempFile, endpointsFiles, doc);
/* /*
Replace some error codes with own error codes, as described in error_codes.txt Replace some error codes with own error codes, as described in error_codes.txt
*/ */
const to_replace = { const to_replace = {
401: 'login error (invalid cookie)', "401": "login error (invalid cookie)",
402: 'login error (bad cookie)', "402": "login error (bad cookie)",
403: 'login error (no cookie)', "403": "login error (no cookie)",
"410": "argument/data error",
"411": "argument/data error",
"412": "argument/data error",
"413": "argument/data error",
"414": "argument/data error",
"415": "argument/data error",
"416": "argument/data error",
"417": "argument/data error",
"418": "argument/data error",
"419": "argument/data error",
"420": "invalid authetication object",
410: 'argument/data error',
411: 'argument/data error',
412: 'argument/data error',
413: 'argument/data error',
414: 'argument/data error',
415: 'argument/data error',
416: 'argument/data error',
417: 'argument/data error',
418: 'argument/data error',
419: 'argument/data error',
420: 'invalid authetication object',
} }
let file = JSON.parse(fs.readFileSync(tempFile, 'utf8')) let file = JSON.parse(fs.readFileSync(tempFile, 'utf8'));
for (let path in file.paths) { for (let path in file.paths) {
for (let method in file.paths[path]) { for (let method in file.paths[path]) {
for (let response in file.paths[path][method].responses) { for (let response in file.paths[path][method].responses) {
if (to_replace[response]) { if (to_replace[response]) {
file.paths[path][method].responses[response].description = file.paths[path][method].responses[response].description = to_replace[response];
to_replace[response] }
}
}
} }
}
} }
file = JSON.stringify(file) file = JSON.stringify(file);
console.log(file) console.log(file)
fs.writeFileSync(outputFile, file) fs.writeFileSync(outputFile, file);
fs.rmSync(tempFile) fs.rmSync(tempFile);

View File

@ -1,91 +1,54 @@
-----BEGIN PGP PUBLIC KEY BLOCK----- -----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGN43xkBEAC4Xso+Ck3rLswQD9th/FaOkhk0gI+SQB9MJ/0AOwsX1vgvsCpR xsFNBGN43xkBEAC4Xso+Ck3rLswQD9th/FaOkhk0gI+SQB9MJ/0AOwsX1vgv
YrnbSb1QWbaNf5Q7eK3bZnkSp/KqbnY36qZWk0roGFnsGpFQV4ydSSXQQy+4EsHU sCpRYrnbSb1QWbaNf5Q7eK3bZnkSp/KqbnY36qZWk0roGFnsGpFQV4ydSSXQ
qlizrVs0dnn8bFejzUO6cT/JLx6E9NX8Vg699B0c7Ga3L4/qJAQ6gvf6c7x4jSBW Qy+4EsHUqlizrVs0dnn8bFejzUO6cT/JLx6E9NX8Vg699B0c7Ga3L4/qJAQ6
dlmyIEBC2DlMHuV7GOwzbglbfRjt89CeO/AipnI62H34vXIv/xW8Zd8S+hfhPSch gvf6c7x4jSBWdlmyIEBC2DlMHuV7GOwzbglbfRjt89CeO/AipnI62H34vXIv
co04g2oC2dyQ74FU8iKaBb+n+NMc06sT+a+lHLja9mjVc1/KQ3QYsSfdA3xkeQhW /xW8Zd8S+hfhPSchco04g2oC2dyQ74FU8iKaBb+n+NMc06sT+a+lHLja9mjV
XxLKDyEp6yd4le2kSr0MXfsQbTa7bvn26CI3i5YErnsAJhpzuRJaao/D/sik0YXJ c1/KQ3QYsSfdA3xkeQhWXxLKDyEp6yd4le2kSr0MXfsQbTa7bvn26CI3i5YE
2Bw0ImOgvmgJmaI7jQiQU4j5Kv+LCHXm7ZfO33X0rc/xkVjAxllXTKRy86IkIBB3 rnsAJhpzuRJaao/D/sik0YXJ2Bw0ImOgvmgJmaI7jQiQU4j5Kv+LCHXm7ZfO
c3iOfucNmyw26W5hnuazs3sDbk+dVhrhdB1lMMVNvZ6qQTrt2WVjlJjaCgvK9Lel 33X0rc/xkVjAxllXTKRy86IkIBB3c3iOfucNmyw26W5hnuazs3sDbk+dVhrh
7F+lpDA4LdoqX+sDoR/b0z33HGgn6BP+xWbTXjCWycs39d+xC/2x4WlWcePT2YXK dB1lMMVNvZ6qQTrt2WVjlJjaCgvK9Lel7F+lpDA4LdoqX+sDoR/b0z33HGgn
rYkWBHIazCVO0tSQEGwREbE1oc2Ll5niAho3DUqIqwSR1qmlPHBAaix1ctMe24in 6BP+xWbTXjCWycs39d+xC/2x4WlWcePT2YXKrYkWBHIazCVO0tSQEGwREbE1
rqoU8ef7qEY8qlLgSoz7+DTmJOcTJILU3mN3rTwnfGCcWdFKZUUAWU0WrwARAQAB oc2Ll5niAho3DUqIqwSR1qmlPHBAaix1ctMe24inrqoU8ef7qEY8qlLgSoz7
tCZGbG9yaWFuIEtyb3R0ZW5kb3JmZXIgPGlwb3N0QGR1Y2suY29tPokCNQQQAQgA +DTmJOcTJILU3mN3rTwnfGCcWdFKZUUAWU0WrwARAQABzSZGbG9yaWFuIEty
HwUCY3jfGQYLCQcIAwIEFQgKAgMWAgECGQECGwMCHgEACgkQezMi6cno/2R3tRAA b3R0ZW5kb3JmZXIgPGlwb3N0QGR1Y2suY29tPsLBdQQQAQgAHwUCY3jfGQYL
t+SVp7/jgDgxigm+HIaN7tdUiW7/5QhB6FgOVbI2FPkd45onL3EO9PjSvC0TlW6L CQcIAwIEFQgKAgMWAgECGQECGwMCHgEACgkQezMi6cno/2R3tRAAt+SVp7/j
ObxkAnE09bMobO6LTfSp++P8hcW2wCDMyzamZMqqJ+mNXWVIGXOL1rEHQCz+WS7v gDgxigm+HIaN7tdUiW7/5QhB6FgOVbI2FPkd45onL3EO9PjSvC0TlW6LObxk
BXvwBqhuIumWCQlrSKxvFjVKqqXk1epzSMm410oXGivEtUDsmYkFCmGCTOCpwANQ AnE09bMobO6LTfSp++P8hcW2wCDMyzamZMqqJ+mNXWVIGXOL1rEHQCz+WS7v
x7CFuDHhktV/TE9QS/vH7Z2MOKVzyOqWX5by+8BQ6wSPhyiB+XyUITDnWujrUBFo BXvwBqhuIumWCQlrSKxvFjVKqqXk1epzSMm410oXGivEtUDsmYkFCmGCTOCp
pXhvw2MSjxsKxVrBS1Ddvf0lKBPlhS9Hif0eoolRiFC5sMpVPO+aAwEi6+8woMgs wANQx7CFuDHhktV/TE9QS/vH7Z2MOKVzyOqWX5by+8BQ6wSPhyiB+XyUITDn
+oHohRxxnYkfnyvQEnTSHJa5qsWCsL3bqisNShw6d7YJgIfS6BTM0KMufFBlCt7S WujrUBFopXhvw2MSjxsKxVrBS1Ddvf0lKBPlhS9Hif0eoolRiFC5sMpVPO+a
6DtJDalWe8nGkIy/FYWqnli4DUmUAs67YiRC3bN+d2b4T7+89dRxJSGTfhQRlHlr AwEi6+8woMgs+oHohRxxnYkfnyvQEnTSHJa5qsWCsL3bqisNShw6d7YJgIfS
LpX6c3qKK/DL+4oa0v0Pb35ur58uVXuO7MfRPI0cR3/zNfhH7dZKCkUYFDezJEC6 6BTM0KMufFBlCt7S6DtJDalWe8nGkIy/FYWqnli4DUmUAs67YiRC3bN+d2b4
vodRe/ChFndBuKJcg8X4VJYANbiRxkVet5gnbsATX+1BMM2tzQXqt1kF2ontDvRn T7+89dRxJSGTfhQRlHlrLpX6c3qKK/DL+4oa0v0Pb35ur58uVXuO7MfRPI0c
JSdC4uF7eH8e4i9EvXm2MnYK5ygkEDUDRLwgP/AJ+b5mBK4llqJHihPu9fwvGkcJ R3/zNfhH7dZKCkUYFDezJEC6vodRe/ChFndBuKJcg8X4VJYANbiRxkVet5gn
HQRGWD8+lqTk31dcmXk0rv+hQ0g2w0eud+UK2aEpJLu0LEZsb3JpYW4gS3JvdHRl bsATX+1BMM2tzQXqt1kF2ontDvRnJSdC4uF7eH8e4i9EvXm2MnYK5ygkEDUD
bmRvcmZlciA8c2VjdXJpdHlAaXBvc3Qucm9ja3M+iQIyBBABCAAcBQJjiydCBgsJ RLwgP/AJ+b5mBK4llqJHihPu9fwvGkcJHQRGWD8+lqTk31dcmXk0rv+hQ0g2
BwgDAgQVCAoCAxYCAQIbAwIeAQAKCRB7MyLpyej/ZDkdD/4j6eE1gXNrPC2iHuw8 w0eud+UK2aEpJLvOwU0EY3jfGQEQAL3e0pyIiaFcUtmAYIBgSj6FsDuTaQI0
lCMQTiV4NNlzwbzzqafPB5vgdX4/kLdyoSHDvKHaUpjdqLR0AwMO4biguvUQfa5a 8dZJwiARztVyoaDUPKBdZfKdZpUatNvsvJig4LSGrrdN3kM2ZJGOq4wVTL7I
y8eMdWX8kc4QJpGI7itQvZrQB+Y7d+YyLhv0iv3ATw5akn54elgpLqpiBXkRUYjz M33UYc7sJ2SGEsbYLICDskNM0eIdU2sCb0IO8P5AbbTHt6uSogNI3Emfvyf8
mRMSMG1uHYEyb8ki3FbZLHvao/hp1iSbQXHECiIK6MM1XddCy4lzYJppFt1mn54n VQLF8rSrfkvDmkeXTpsdvTQMaX9IrJpWkhTgHQ2wFdhUX/w1A9c9eRBvoZvV
bVM4Aeg1ItBsig/uttw5DA7cg5VKZFdCjJTM9frNON/tw+HdAWdjFdLadKmHqd2O /MjQGbChQG9prG+iv4mqoxjUx1GU1r6DY1a9MvINXrWVS4/NuDLIu2ZcP6Rd
JguwU58bfaLSscAav9JW34E/xU/LV9eydjFM37Ckel7HWyNL+qAmaylGdo437voz Q8DxvAwNH435fw1izCFrX9NrRVJMKTrvdQa4XsycjQM1OczFAkK+2c1EOrK5
XxnSdoTxxksxDKewlpS2jITKHsRv29JTzARfujHvfgjs9kqG5V8tU8UJKa9Ug/nA KWShg6xBrzBaKhbaCUhFhN4Xf2UQdQxQQc82t6JlkddWIY8WRZv8g3QFyBqB
j2qlKNYeOI907xlB+Judic2E7ZRBPq2ZBFEEGja5xUD0fW+bK46AWowfLwAq4nb3 B2jETVHGEroY60T9PS0+55JHL8+YmYZjQcLz13vR3Tdsal/smGrr2j5k9dwQ
N/Pes0cr0VI0VMLVGRgyhVDJI0kUE+MIwrz5QQqhH0aVLi2YLBpCq2STRokMa2HG NQiV9tS9GI1/rXsbiyFPdIq3P0QudsQ3ODe8jKYnlt1ojXbfi2ee8I5xjiAb
Ie/cvUUVlJWSBxmfTfVV+qg+IAjW96HSyh5BkBsoHAVBjOt4fArCZuLYpdLMGXex a4MO4HQ6WsW492YASxdroDqdKl71uahAQ7/Mta+To4U1xXRsI6I0/SnJ+O4u
atcm1sUFXGeEG57JSbZVafqsXZVotHD7jh3xyOwdoSEyUdSy9vXFhxsOE8je/MwS ZPSX3jUseeioMcXV2fWYmY/G4hy8w/6iN5pBogKvW9DFzANOiW5WJ3TGWeZx
LmQiun69tYzPuE727r1nugRsSLQwRmxvcmlhbiBLcm90dGVuZG9yZmVyIDxjb2Rl ReA6jZCYl4kX/vuh+Vc42yNkaYpMrDuN8/Bwt0wkKh1xcu3g4XnJABEBAAHC
MDAybG92ZXJAaXBvc3Qucm9ja3M+iQJHBBABCAA7BYJmKWUKBAsJBwgJkHszIunJ wV8EGAEIAAkFAmN43xkCGwwACgkQezMi6cno/2QjQhAAk0ofeYGhgYso0Pp/
6P9kAxUICgQWAAIBApsDAh4BFiEELYJB5RMO+v98n9zZezMi6cno/2QAAAW0D/wJ 4fOfZF+kgjx/dFLhE5I3wrYETpffw4xSUvU2MwGa6qyxPaWz4jI6BXQ13mbB
/+LgY6DMnRsMqSjeQW54DfA8DpbZ7i3nNRpvJd2cEtKutO9giaGCrXM0PpSUo8nV tgtUDRVM1EeGsRqXxhrMfc/IiwRNOKf+x7Dqoup4C3tGTCRNAuZY2qs6Og26
KMs0RGn99riiOsuDK/2s5ozF11Bb0yVorcBS9Pf5Iy27e2tQekgEafo61P0SuJEo AOPy4pUv/ebe75IsWnX2yEP5SEmzBOKHPaBl6SfVIqms9v84kIkDj0vbi4vo
CeHS+roaHoDqgjFLsrlKpnpqIS66nDs7QQI8D+OGxvBrX4xQ3OHf07aopU5JdtVd 4sJn2niEAzhzNUFELxrb1r7G7RsBGZYiRh9dj1pLMMVGwSdidxpw0zYJO1Sq
vDezBPSssl5ZEi0GpC9E7jxJ1SVKf8NN0lOD6I4Xos15mqpH/omHau5vjWjlqwsi td8e0emMaQwXd0aS74VNRWD6UWN5m2gjVRwPCeidW1KaUabGczB3l672jxVa
sWJKFfQcHc2tyS5Kg4E0PyAZYQXilHwy9CN4D4jmF+uxmF91F42lVla1s6hAtMw6 UKWwWgsaUVWdcl7Bxw09bElAC0/Y1Vnx+h2nXIBR3QWJ2pSz9GBqUv+yngdQ
xRoHeq42GL5tR8DWxLLqH6UH2Z/x2pkHBF1Xb+PeVcEPdcZGCTTjWsAyNUgJULcU WFw4An4BP70D2XrO43gjAjfjiiM4AYi9+uoj75yzpKDB6jIvblsnvkcGYldK
TFTkeMuVeb3a0qkCcs6+ijlBNk10aJMs2slPvjyfS+fCKW3b08YhgJeche24PinU PJ99hGdBWwkbe+euUmdXn5R3obsI4gxG8VZk/1bmWo0KnwTTqazoxh1fppmc
IF9NRpSv2Xg+5GS2Jq3DUwNRUbIH+Ow2cqHCQGOh+kTib2s39mtpSyXdJxxgkuFd 33ASk/TOayw2BPOV1SsiGPOT/uQkZY01EfrmcLtCZuMg8xlxfH8sih56rVGH
rdgZldNCoI9QLQOKQftcctvZnD47LQqsSFwUtgYgN2bycAt6bi5fgNHS2TaYBtSL CT2qh912bPoEFfY/qa/8PtdWZBocN3wjvjIXkg/SRax4/+G/kpMV1mjrYv6l
X+mnFX6ylUN2VwrRkskr1jqMIcThPqdsi7VCL3o17IyenT9rU8w2qURw14O8qtsZ VqAAKnJi+SVxt5Zbhq/X+EfS/DdaDb6914ijcoSgQo3UG8yJv8ZQ8td9TVRF
CYje//9gHGoFuu6V80Wij6kJHX34UAdJBg6FYpYbdLQrRmxvcmlhbiBLcm90dGVu Hb4eOog=
ZG9yZmVyIDxkaXNjb3JkQGlwb3N0LnJvY2tzPokCRwQQAQgAOwWCZzHzbAQLCQcI =92A+
CZB7MyLpyej/ZAMVCAoEFgACAQKbAwIeARYhBC2CQeUTDvr/fJ/c2XszIunJ6P9k
AAALcA//Z1PYIyULARUsz70ASdU/xa2RM+z3RKKxpw8HfEphzqX2L7pLsJ8+dlfR
yukrabmF5VMxsKCCULT+zhna7/K43A2wSlMtXov1rE/XsUhi8ly3paPEnHew+zrP
KwZUuZ1k8F9DVQDb3rdKgvKr88XCHyUsb2MEWZkPzXa2ZgKlu0GsEaYF0RTl7X/n
xIoh/xEvqlpY870W56tFyDGxIxRszaaVIx6Vmguw+Gb6Gjeuwp6CjHdVjiRZZswQ
4GKhV/2n6jnLrmw8lBMSaNaOxO+OmwFkb2ZnEkv/KwEEwPaBGj574DwDODPel4ej
LWh94RjTwB3B1KX9bEkS35+kJWhvRD1+BAhVFMNyPwguFZfYKB3o5TP3TnKjX+5D
QB7CIEqab5j3DcKD+h0aTPzPna6XA6NLK69FzKaai+dwosjU2OOCh0L3CtkzNT69
Q9R7KVR70pkxEq07FBJr18ZUrkQmVvNhWxGK3b54mb/lyWa5cjS9qe8THWl3x69n
N/nP9LRNx5BqBkgUifkqLjIMpsqBdk52Iiq8kUOSxl2wb0uxl8OmIMRY1ujr4hN+
+R6YTHQxFqgd7XCA+m0548jVw9tVdrcaCkYsVKmlPMStY0faeOlav98RGBYp5Ae8
9lPdvfyvu9hu0HfYYGji9HHt+QgbzpyUe1/V0mXRlLk6UJ6Ai7y5Ag0EY3jfGQEQ
AL3e0pyIiaFcUtmAYIBgSj6FsDuTaQI08dZJwiARztVyoaDUPKBdZfKdZpUatNvs
vJig4LSGrrdN3kM2ZJGOq4wVTL7IM33UYc7sJ2SGEsbYLICDskNM0eIdU2sCb0IO
8P5AbbTHt6uSogNI3Emfvyf8VQLF8rSrfkvDmkeXTpsdvTQMaX9IrJpWkhTgHQ2w
FdhUX/w1A9c9eRBvoZvV/MjQGbChQG9prG+iv4mqoxjUx1GU1r6DY1a9MvINXrWV
S4/NuDLIu2ZcP6RdQ8DxvAwNH435fw1izCFrX9NrRVJMKTrvdQa4XsycjQM1OczF
AkK+2c1EOrK5KWShg6xBrzBaKhbaCUhFhN4Xf2UQdQxQQc82t6JlkddWIY8WRZv8
g3QFyBqBB2jETVHGEroY60T9PS0+55JHL8+YmYZjQcLz13vR3Tdsal/smGrr2j5k
9dwQNQiV9tS9GI1/rXsbiyFPdIq3P0QudsQ3ODe8jKYnlt1ojXbfi2ee8I5xjiAb
a4MO4HQ6WsW492YASxdroDqdKl71uahAQ7/Mta+To4U1xXRsI6I0/SnJ+O4uZPSX
3jUseeioMcXV2fWYmY/G4hy8w/6iN5pBogKvW9DFzANOiW5WJ3TGWeZxReA6jZCY
l4kX/vuh+Vc42yNkaYpMrDuN8/Bwt0wkKh1xcu3g4XnJABEBAAGJAh8EGAEIAAkF
AmN43xkCGwwACgkQezMi6cno/2QjQhAAk0ofeYGhgYso0Pp/4fOfZF+kgjx/dFLh
E5I3wrYETpffw4xSUvU2MwGa6qyxPaWz4jI6BXQ13mbBtgtUDRVM1EeGsRqXxhrM
fc/IiwRNOKf+x7Dqoup4C3tGTCRNAuZY2qs6Og26AOPy4pUv/ebe75IsWnX2yEP5
SEmzBOKHPaBl6SfVIqms9v84kIkDj0vbi4vo4sJn2niEAzhzNUFELxrb1r7G7RsB
GZYiRh9dj1pLMMVGwSdidxpw0zYJO1Sqtd8e0emMaQwXd0aS74VNRWD6UWN5m2gj
VRwPCeidW1KaUabGczB3l672jxVaUKWwWgsaUVWdcl7Bxw09bElAC0/Y1Vnx+h2n
XIBR3QWJ2pSz9GBqUv+yngdQWFw4An4BP70D2XrO43gjAjfjiiM4AYi9+uoj75yz
pKDB6jIvblsnvkcGYldKPJ99hGdBWwkbe+euUmdXn5R3obsI4gxG8VZk/1bmWo0K
nwTTqazoxh1fppmc33ASk/TOayw2BPOV1SsiGPOT/uQkZY01EfrmcLtCZuMg8xlx
fH8sih56rVGHCT2qh912bPoEFfY/qa/8PtdWZBocN3wjvjIXkg/SRax4/+G/kpMV
1mjrYv6lVqAAKnJi+SVxt5Zbhq/X+EfS/DdaDb6914ijcoSgQo3UG8yJv8ZQ8td9
TVRFHb4eOog=
=VNlN
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----

View File

@ -4,7 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>IPost Privacy Policy</title> <title>IPost Privacy Policy</title>
<meta name="description" content="IPosts PrivacyPolicy"> <meta name="description" content="IPosts PrivacyPolicy">
<%- newrelic %>
<script> <script>
<%- warnmessagejs %> <%- warnmessagejs %>
</script> </script>

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>IPost Terms of Service</title> <title>IPost Terms of Service</title>
<%- newrelic %>
<meta name="description" content="IPosts Terms of Service"> <meta name="description" content="IPosts Terms of Service">
<script src="/js/addnavbar.js" charset="utf-8"></script> <script src="/js/addnavbar.js" charset="utf-8"></script>
<script src="/js/warn_message.js" charset="utf-8"></script> <script src="/js/warn_message.js" charset="utf-8"></script>

View File

@ -11,7 +11,7 @@
<% if(user.username === undefined) { %> <% if(user.username === undefined) { %>
<script> document.location.href = '/no_login?r='+encodeURIComponent(document.location.pathname) </script> <script> document.location.href = '/no_login?r='+encodeURIComponent(document.location.pathname) </script>
<% } %> <% } %>
<%- newrelic %>
<style> <style>
<%- globalcss %> <%- globalcss %>
<%- loadfile("./css/posts.css") %> <%- loadfile("./css/posts.css") %>

View File

@ -4,6 +4,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IPost</title> <title>IPost</title>
<meta name="description" content="<%-unauthorized_description%>"> <meta name="description" content="<%-unauthorized_description%>">
<%- newrelic %>
<style> <style>
<%- globalcss %> <%- globalcss %>
<%- loadfile("./css/logon.css") %> <%- loadfile("./css/logon.css") %>

View File

@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login | IPost</title> <title>Login | IPost</title>
<meta name="description" content="Chat on IPost by logging in today"> <meta name="description" content="Chat on IPost by logging in today">
<%- newrelic %>
<link rel="stylesheet" href="/css/logon.css"> <link rel="stylesheet" href="/css/logon.css">
<script src="/js/warn_message.js" charset="utf-8"></script> <script src="/js/warn_message.js" charset="utf-8"></script>
<script src="/js/addnavbar.js" charset="utf-8"></script> <script src="/js/addnavbar.js" charset="utf-8"></script>

View File

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>USERS Page</title> <title>USERS Page</title>
<meta name="description" content="view other users pages on IPost today"> <meta name="description" content="view other users pages on IPost today">
<%- newrelic %>
<link rel="stylesheet" href="/css/style.css"> <link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/css/global.css"> <link rel="stylesheet" href="/css/global.css">
<script src="/js/addnavbar.js" charset="utf-8"></script> <script src="/js/addnavbar.js" charset="utf-8"></script>

View File

@ -9,7 +9,7 @@
<meta name="description" content="Chat on IPost now"> <meta name="description" content="Chat on IPost now">
<% } %> <% } %>
<title>Posts | IPost</title> <title>Posts | IPost</title>
<%- newrelic %>
<style> <style>
<%- globalcss %> <%- globalcss %>
<%- loadfile("./css/posts.css") %> <%- loadfile("./css/posts.css") %>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" dir="ltr"> <html lang="en" dir="ltr">
<head> <head>
<%- newrelic %>
<link rel="stylesheet" href="/css/logon.css"> <link rel="stylesheet" href="/css/logon.css">
<script src="/js/warn_message.js" charset="utf-8"></script> <script src="/js/warn_message.js" charset="utf-8"></script>
<script src="/js/addnavbar.js" charset="utf-8"></script> <script src="/js/addnavbar.js" charset="utf-8"></script>

View File

@ -10,7 +10,7 @@
<% } else { %> <% } else { %>
<meta name="description" content="search IPost now"> <meta name="description" content="search IPost now">
<% } %> <% } %>
<%- newrelic %>
<link rel="stylesheet" href="/css/search.css"> <link rel="stylesheet" href="/css/search.css">
<script type="text/javascript" src="/js/htmlescape.js"></script> <script type="text/javascript" src="/js/htmlescape.js"></script>
<link rel="stylesheet" href="/css/global.css"> <link rel="stylesheet" href="/css/global.css">

View File

@ -13,7 +13,7 @@
<% if(user.username === undefined) { %> <% if(user.username === undefined) { %>
<script> document.location.href = '/no_login?r='+encodeURIComponent(document.location.pathname) </script> <script> document.location.href = '/no_login?r='+encodeURIComponent(document.location.pathname) </script>
<% } %> <% } %>
<%- newrelic %>
<link rel="stylesheet" href="/css/style.css"> <link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/css/global.css"> <link rel="stylesheet" href="/css/global.css">
<script src="/js/addnavbar.js" charset="utf-8"></script> <script src="/js/addnavbar.js" charset="utf-8"></script>

View File

@ -13,7 +13,7 @@
<% if(user.username === undefined) { %> <% if(user.username === undefined) { %>
<script> document.location.href = '/no_login?r='+encodeURIComponent(document.location.pathname) </script> <script> document.location.href = '/no_login?r='+encodeURIComponent(document.location.pathname) </script>
<% } %> <% } %>
<%- newrelic %>
<style> <style>
<%- globalcss %> <%- globalcss %>
<%- loadfile("./css/style.css") %> <%- loadfile("./css/style.css") %>