152 Commits
0.3.0 ... 0.4.0

Author SHA1 Message Date
Jeff Becker
9a75911ab7 update mod ui 2016-03-12 11:24:04 -05:00
Jeff Becker
8181d174a5 add hr in connection state 2016-03-12 10:18:43 -05:00
Jeff Becker
c618440878 span -> div 2016-03-12 10:16:45 -05:00
Jeff Becker
0bc6298cc0 update mod ui to have more details 2016-03-12 10:15:56 -05:00
Jeff Becker
93b452c497 update mod ui feed connection elements 2016-03-12 10:13:17 -05:00
Jeff Becker
b2d46c9021 fix typo 2016-03-12 09:45:31 -05:00
Jeff Becker
f972d91137 update mod panel connections view 2016-03-12 09:44:51 -05:00
Jeff Becker
f8a6b32b5f add expiration of orphaned articles command to mod page 2016-03-12 08:29:57 -05:00
Jeff Becker
439ab7583a add synchronize feeds command in mod panel 2016-03-12 07:56:29 -05:00
Jeff Becker
5800b1bf1d add note about redis version 2016-03-11 20:16:10 -05:00
Jeff Becker
1ba53b7688 update archiver to support multiple attachments 2016-03-10 10:07:19 -05:00
Jeff Becker
dd2eb90230 update post fail page 2016-03-09 13:27:34 -05:00
Jeff Becker
2319327917 update older templates to be aware of themes 2016-03-09 13:23:58 -05:00
Jeff Becker
89dfe68b32 update bloodgod theme for 404 page 2016-03-09 13:21:19 -05:00
Jeff Becker
c4dadd21fc update 404 page 2016-03-09 13:16:46 -05:00
Jeff Becker
812d7cfd9d 404 page 2016-03-09 12:54:21 -05:00
Jeff Becker
415f0d6e39 update install.sh to have build options passed into build.sh 2016-03-09 10:59:22 -05:00
Jeff Becker
08d97147b8 add simple install script 2016-03-09 10:41:24 -05:00
Jeff Becker
23614275eb spacing 2016-03-09 10:05:52 -05:00
Jeff Becker
1eca5c59e3 update readme irc links 2016-03-09 10:05:23 -05:00
Jeff Becker
91953148e8 update readme to point to build instructions 2016-03-09 08:41:37 -05:00
Jeff Becker
c5a5fb57d9 update build documentation 2016-03-09 08:40:18 -05:00
Jeff Becker
5b8d338840 add --disable-redis option to build.sh 2016-03-09 08:36:44 -05:00
Jeff Becker
5bc7c92e71 only apply theme if parameters are correct 2016-03-08 20:35:03 -05:00
Jeff Becker
f32bd8c946 update dayman theme 2016-03-08 18:18:28 -05:00
Jeff Becker
ca333e06ac update dayman theme 2016-03-08 18:16:27 -05:00
Jeff Becker
9aab7addb1 update dayman theme 2016-03-08 18:11:48 -05:00
Jeff Becker
0b984d0dcd color -> background-color 2016-03-08 18:10:03 -05:00
Jeff Becker
73b254367f update dayman theme 2016-03-08 18:09:27 -05:00
Jeff Becker
a9c09e57e9 dayman css initial 2016-03-08 18:02:33 -05:00
Jeff Becker
d8b57e2189 add default theme (yostuba) option 2016-03-08 17:02:23 -05:00
Jeff Becker
7ce22cefb7 try again inverting image colors via css 2016-03-08 16:58:47 -05:00
Jeff Becker
2de555f176 invert origin image color in bloodgod theme 2016-03-08 16:53:29 -05:00
Jeff Becker
68c071e318 don't use main/onload for theme application 2016-03-08 10:45:39 -05:00
Jeff Becker
077b61270c navbar on front page 2016-03-08 08:42:52 -05:00
Jeff Becker
5975383349 have navbar theme changer text underlined 2016-03-08 08:35:34 -05:00
Jeff Becker
46ca31066f add hover for navbar theme changers 2016-03-08 08:34:07 -05:00
Jeff Becker
9db627d07f update themes to be more consistant 2016-03-08 08:31:12 -05:00
Jeff Becker
014dbdfa95 add navbar to ukko 2016-03-08 08:25:31 -05:00
Jeff Becker
ea06f5498f add nntpchan.js stuff to mod panel 2016-03-08 08:13:57 -05:00
Jeff Becker
c2d60063ee add onload to modpage 2016-03-08 08:13:00 -05:00
Jeff Becker
0ebd2241e5 fix mod.js error 2016-03-08 07:25:54 -05:00
Jeff Becker
0aca177c86 fix color in bloodgod theme 2016-03-08 07:24:29 -05:00
Jeff Becker
9bb45cd896 update bloodgod theme 2016-03-08 07:23:18 -05:00
Jeff Becker
7458a884d5 add onload to frontpage too 2016-03-08 07:17:48 -05:00
Jeff Becker
6392a606c4 add onload to body 2016-03-07 21:00:54 -05:00
Jeff Becker
693bcdeffd try fixing localstorage again 2016-03-07 20:57:14 -05:00
Jeff Becker
81a398ab26 don't check for non existant key in localstorage 2016-03-07 20:55:46 -05:00
Jeff Becker
7296bc6944 fix localstorage 2016-03-07 20:55:05 -05:00
Jeff Becker
e97c4bab53 fix onload 2016-03-07 20:51:41 -05:00
Jeff Becker
2c03b8fd6b persist theme changes 2016-03-07 20:50:58 -05:00
Jeff Becker
2f3742fa7c update bloodgod theme 2016-03-07 20:41:10 -05:00
Jeff Becker
ff816fd556 update bloodgod theme 2016-03-07 20:37:38 -05:00
Jeff Becker
585d4cbff1 fix up bloodgod theme 2016-03-07 20:35:28 -05:00
Jeff Becker
0d388b19ee have bloodgod theme include buttons 2016-03-07 20:34:19 -05:00
Jeff Becker
a8e870a099 update bloodgod theme to include inputs and textares 2016-03-07 20:32:08 -05:00
Jeff Becker
32c4dcbe69 update bloodgood theme to apply to navbar 2016-03-07 20:29:00 -05:00
Jeff Becker
a3571f541b fix bloodgod theme background 2016-03-07 20:27:14 -05:00
Jeff Becker
39dbe314d6 ammend theme changing 2016-03-07 20:25:29 -05:00
Jeff Becker
63e2614983 try fixing theme switcher 2016-03-07 20:21:20 -05:00
Jeff Becker
0623077a40 add more themes and support for switching themes 2016-03-07 20:18:28 -05:00
Jeff Becker
f05d43af89 fix 2016-03-07 12:05:52 -05:00
Jeff Becker
68b73ff1dd fix 2016-03-07 12:03:27 -05:00
Jeff Becker
0613350420 fix 2016-03-07 12:00:32 -05:00
Jeff Becker
37af910c89 more nntp feed stuffs 2016-03-07 11:59:33 -05:00
Jeff Becker
6286a6529c disable checking for mod handler 2016-03-07 11:50:16 -05:00
Jeff Becker
51bdf9609f setinterval was backwards 2016-03-07 11:44:42 -05:00
Jeff Becker
b5d60f1431 syntax error 2016-03-07 11:43:11 -05:00
Jeff Becker
56f8fc8b91 add nntpchan feed ticker to mod panel 2016-03-07 11:42:21 -05:00
Jeff Becker
e0de5dfbf5 russian translations, thanks siger 2016-03-01 18:30:20 -05:00
Jeff Becker
d122da98b5 float: left for attachments 2016-02-29 09:07:42 -05:00
Jeff Becker
e6a38af5ee sorround attachments in div 2016-02-29 09:06:13 -05:00
Jeff Becker
abf3da5900 multiple files 2016-02-29 08:35:26 -05:00
Jeff Becker
a40aad33d1 * add 2 file support
* TODO: add nicer widget for multifile
2016-02-29 08:14:19 -05:00
Jeff Becker
9a6f3ef4ed add portugeese translations, thanks pyr 2016-02-28 10:16:47 -05:00
Jeff Becker
372a8767ee add es tranlsations, thanks trolly 2016-02-28 09:47:19 -05:00
Jeff
f7a31012e8 Merge pull request #48 from ZiRo-/master
Add internationalization
2016-02-28 08:13:55 -05:00
ZiRo
5cf11d50d4 Fix date format 2016-02-28 14:00:22 +01:00
ZiRo
a4f3e4ffa4 Fix date format 2016-02-28 13:59:38 +01:00
ZiRo
60de698f2c Add German translation 2016-02-28 13:57:51 +01:00
ZiRo
1c4f4630c2 localize templates 2016-02-28 13:40:16 +01:00
ZiRo
9974628f62 Make it possible to add more parameters to post template 2016-02-28 10:19:55 +01:00
ZiRo
2ea928417d Localize time formats 2016-02-28 09:48:07 +01:00
ZiRo
eede0c55dc Merge branch 'master' of github.com:ZiRo-/nntpchan 2016-02-28 08:55:33 +01:00
ZiRo
c6780cfd27 Merge remote-tracking branch 'upstream/master' 2016-02-28 08:54:23 +01:00
ZiRo
34b2e18587 Add dummy translation files 2016-02-28 08:54:05 +01:00
Jeff
38d3bc4481 Merge pull request #47 from ZiRo-/patch-1
Add RPC
2016-02-26 07:53:49 -05:00
ZiRo-
e556d6d2be Add RPC 2016-02-25 20:25:10 +01:00
ZiRo-
22a3564f22 Merge pull request #1 from majestrate/master
rebase
2016-02-25 20:02:06 +01:00
Jeff Becker
b695d97e82 even out front page 2016-02-22 07:39:06 -05:00
Jeff Becker
64e4c3320d fix front page 2016-02-22 07:32:49 -05:00
Jeff Becker
7c7d07434d undo changes with post form 2016-02-21 17:08:38 -05:00
Jeff Becker
d2b9e76389 update templates 2016-02-21 16:51:56 -05:00
Jeff Becker
f91bd642a5 fix moderate.js 2016-02-20 09:42:22 -05:00
Jeff Becker
9459e1758d fix moderate.js tool 2016-02-20 09:41:03 -05:00
Jeff Becker
24b2bd8e15 add moderate example script 2016-02-20 09:35:20 -05:00
Jeff
8ad13c8e45 Merge pull request #46 from ZiRo-/master
add images to catalog; style catalog properly
2016-02-20 13:54:16 +00:00
ZiRo
c6e6c2e5d6 Catalog improvements 2016-02-20 11:16:54 +01:00
Jeff
9690156fbf Merge pull request #45 from ZiRo-/master
Add catalog
2016-02-19 15:57:01 +00:00
ZiRo
dea611c1ba initial for catalog 2016-02-19 16:48:41 +01:00
Jeff Becker
0e0f35e6dc fix legal footer 2016-02-17 14:13:58 -05:00
Jeff Becker
5370898da9 * add legal notice
* fix front page graph
2016-02-17 14:10:50 -05:00
ZiRo
1020fae1dd Add Board titles 2016-02-17 15:31:28 +01:00
ZiRo
6bcfff0269 post form fixes 2016-02-17 15:06:00 +01:00
ZiRo
5fbf75a77d Add update link 2016-02-17 14:59:39 +01:00
ZiRo
86744c9349 fix posting form 2016-02-17 14:51:25 +01:00
ZiRo
24b6d87381 add page list to the bottom of board pages 2016-02-17 11:30:19 +01:00
ZiRo
649cc96269 add footer links to threads 2016-02-17 10:53:20 +01:00
ZiRo
a9e377563d Fix board list a bit 2016-02-17 10:27:16 +01:00
ZiRo
9229aa62bd Merge remote-tracking branch 'upstream/master' 2016-02-16 18:35:34 +01:00
ZiRo
f9c9028a0b The posts and threads are now pretty much done 2016-02-16 16:52:20 +01:00
ZiRo
5fa12dcda8 some more CSS fuckery 2016-02-16 16:37:02 +01:00
ZiRo
8c0a4ac6b0 crop i2p icon 2016-02-16 14:56:59 +01:00
ZiRo
bd279547d2 some css fuckery 2016-02-16 14:48:56 +01:00
Jeff Becker
1ad6432576 make reply button/link obvious 2016-02-16 08:32:51 -05:00
ZiRo
4b007fa759 Use 8chin post borders 2016-02-16 13:59:10 +01:00
ZiRo
abe985b6c5 Make origin icon smaller 2016-02-16 13:47:56 +01:00
ZiRo
6091934884 Make origin icon smaller 2016-02-16 13:47:30 +01:00
ZiRo
7db701b23a Don't use a maring for threads 2016-02-16 13:42:28 +01:00
Jeff Becker
00816c1a95 rename mod action 2016-02-15 17:04:59 -05:00
Jeff Becker
713f8a7d19 fix up ukko 2016-02-15 17:02:38 -05:00
Jeff Becker
da42b8fc7d fix ukko more 2016-02-15 17:00:35 -05:00
Jeff Becker
27bf7de803 style tweaks 2016-02-15 16:55:36 -05:00
Jeff Becker
4fd443dce4 tweaks 2016-02-15 16:53:17 -05:00
Jeff Becker
f8d3f85a4d fix typos 2016-02-15 16:52:38 -05:00
Jeff Becker
007851056f fix ukko 2016-02-15 16:50:52 -05:00
Jeff Becker
f97b5a56e5 uncomment out ukko threads header 2016-02-15 16:48:18 -05:00
Jeff Becker
7495c3e843 fix bad end tag 2016-02-15 16:44:35 -05:00
Jeff Becker
713ff950e7 fix origin icon more 2016-02-15 16:42:42 -05:00
Jeff Becker
64a7a6e6aa fix origin icons 2016-02-15 16:36:56 -05:00
Jeff Becker
6aa9475c91 * move origin image to be inside postheader
* style tweaks
2016-02-15 16:29:54 -05:00
Jeff Becker
a9ac1207c0 * add thumnail class to post thumbnails
* fix typo in css
2016-02-15 15:51:17 -05:00
ZiRo
539033ec6d looking pretty good by now 2016-02-15 21:20:37 +01:00
ZiRo
80041baf1e fix some colors 2016-02-15 20:00:56 +01:00
ZiRo
b83b21df59 some more styling 2016-02-15 19:04:03 +01:00
ZiRo
69c30e0685 no more br 2016-02-15 18:42:46 +01:00
ZiRo
0ed2bad9f1 no more rounded rectangles, stupid boarders or shadows 2016-02-15 18:38:32 +01:00
ZiRo
41d3cb467b seperator line for threads 2016-02-15 18:26:30 +01:00
ZiRo
476ae002c2 beginn style change 2016-02-15 18:14:22 +01:00
ZiRo
6f0b518391 ... and No. for everyone 2016-02-15 17:20:41 +01:00
ZiRo
f673a10768 Reply only for OP 2016-02-15 17:17:00 +01:00
ZiRo
8668d1aa08 fix backlinks & CSS classes 2016-02-15 17:06:59 +01:00
ZiRo
d2babe367b use proper timestamps + fix double prefix
This has the benefit that modern browsers see timestamps in their local time zone
2016-02-15 16:13:28 +01:00
ZiRo
3b5937f2d9 Add my rebuild script to gitignore 2016-02-15 15:54:43 +01:00
Jeff Becker
1896060218 fix post template to include prefix 2016-02-13 17:26:00 -05:00
Jeff Becker
9d2461c716 make build.sh verbose when doing go get 2016-02-09 09:02:33 -05:00
Jeff Becker
85d60a1928 fix up more 2016-02-08 15:05:57 -05:00
Jeff Becker
6eb42d4cf7 fix up lynxchan migrate script such that it exits 2016-02-08 14:09:30 -05:00
Jeff Becker
37df9ef131 fixes to migration script 2016-02-08 13:47:06 -05:00
Jeff Becker
450d07d6e5 fix typo 2016-02-08 13:10:26 -05:00
Jeff Becker
047a63ec20 add initial lynxhub importer 2016-02-08 12:42:25 -05:00
Jeff Becker
3821562cf6 add redis database documentation 2016-02-07 09:36:46 -05:00
49 changed files with 1883 additions and 330 deletions

8
.gitignore vendored
View File

@@ -8,7 +8,8 @@
.\#*
# srnd config files
*.ini
srnd.ini
feeds.ini
# default article store directory
articles
@@ -24,4 +25,7 @@ srndv2
*.key
# certificates
certs
certs
rebuild.sh

View File

@@ -6,25 +6,18 @@ This repository contains resources used by the core daemon which is located [her
## getting started ##
Get the dependancies
sudo apt-get update
sudo apt-get --no-install-recommends install imagemagick libsodium-dev ffmpeg sox build-essential git golang ca-certificates
Check out this repo and build it
git clone https://github.com/majestrate/nntpchan
cd nntpchan
./build.sh
Now configure the database. [Next](doc/database.md)
[see build documentation](doc/build.md)
---
*PLEASE* report any bugs you find while setting up or building [(here)](https://github.com/majestrate/nntpchan/issues) so that the problems get fixed :^)
For peering requests, questions or support find me on [rizon](https://qchat.rizon.net/?channels=#nntpchan) as \__uguu\__
IRC:
[freenode: #nntpchan](https://webchat.freenode.net/?channels=#nntpchan)
[rizon: #nntpchan](https://qchat.rizon.net/?channels=#nntpchan)
Like this project? Fund it:

View File

@@ -1,8 +1,33 @@
#!/usr/bin/env bash
set -e
root=$(readlink -e $(dirname $0))
tags=""
help_text="usage: $0 [--disable-redis]"
# check for help flags first
for arg in $@ ; do
case $arg in
-h|--help)
echo $help_text
exit 0
;;
esac
done
# check for build flags
for arg in $@ ; do
case $arg in
"--disable-redis")
tags="$tags -tags disable_redis"
;;
esac
done
cd $root
export GOPATH=$root/go
mkdir -p $GOPATH
go get -u github.com/majestrate/srndv2
go get -v -u $tags github.com/majestrate/srndv2
cp -a $GOPATH/bin/srndv2 $root
echo "Built"

BIN
contrib/static/404.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 B

After

Width:  |  Height:  |  Size: 135 B

View File

@@ -0,0 +1,84 @@
/**
bloodgod theme css override
*/
body {
color: #666;
background: #111;
}
input, textarea, button, input[type="text"], input[type="password"],
input[type="checkbox"], input[type="file"], input[type="submit"],
input[type="button"] {
color: #666;
background: #050505;
border-color: #D80000;
}
.navbar {
background: #980000;
color: black;
}
#captcha_img, pre {
background: #D80000;
}
.reply, .ukko_thread_header {
border-color: #CA0101;
}
.ukko_thread_header {
border-style: solid;
border-width: 1px;
border-radius: 3px;
}
hr, .name {
color: #6B1919;
}
.subject {
color: #4E0000;
}
.reply , .ukko_thread_header {
background: #0C0C0C;
}
a {
color: #500707;
}
.postno, .postnol {
color: #B70000;
}
.postform >tr, th {
background: #6B1919;
}
table tbody tr:nth-of-type( even ) {
background-color: #430000;
}
table thead th {
color: #6B1919;
background: #430000;
border-color: #270000;
}
.navbar-sep {
color: #270000;
}
.navbar-link > label:hover {
color: red;
}
.origin > img , .not_found > img {
-webkit-filter: invert(1);
filter: invert(1);
}

21
contrib/static/dayman.css Normal file
View File

@@ -0,0 +1,21 @@
/*
dayman
fighter of the night man
champion of the sun
*/
body {
background-color: #FFEFC9;
background-repeat: no-repeat;
background-attachment: fixed;
background-size: 100% 100%;
background-image: url('maymface.png');
}
.reply, .ukko_thread_header, th, .postform > tr , tbody > tr:nth-of-type( even ) {
background: #FBFFC9;
}
.navbar, table, thead, th, table, pre {
background: #FFECBE;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
contrib/static/maymface.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -87,19 +87,25 @@ function nntpchan_admin_board(method) {
})
}
function nntpchan_admin(method, param) {
nntpchan_mod({
name:"admin",
parser: function(target) {
return method;
},
handle: function(j) {
function nntpchan_admin(method, param, handler_cb) {
if (handler_cb) {
// we got a handler already set
} else {
// no handler set
var handler_cb = function(j) {
if (j.result) {
return document.createTextNode(j.result);
} else {
return "nothing happened?";
}
}
}
nntpchan_mod({
name:"admin",
parser: function(target) {
return method;
},
handle: handler_cb,
method: ( param && "POST" ) || "GET",
data: param
})
@@ -136,6 +142,81 @@ function nntpchan_delete() {
});
}
function createConnectionElement(j) {
var e = document.createElement("div");
e.setAttribute("class", "connection");
var auth = document.createElement("div");
auth.appendChild(document.createTextNode("Connection: "+j.name));
// authentication state
if (j.authed) {
auth.setAttribute("class", "authed");
auth.appendChild(document.createTextNode("(authenticated)"));
} else {
auth.appendChild(document.createTextNode("(not authenticated)"));
}
e.appendChild(auth);
// connection mode
var mode = document.createElement("div");
mode.setAttribute("class", "mode");
mode.appendChild(document.createTextNode("mode: "+j.mode));
e.appendChild(mode);
var pending = document.createElement("div");
pending.setAttribute("class", "pending");
// pending articles
var articles = Object.keys(j.pending);
pending.appendChild(document.createTextNode("pending articles: "+articles.length));
for ( var idx = 0 ; idx < articles.length; idx ++ ) {
var msgid = articles[idx];
var state = j.pending[msgid];
var elem = document.createElement("div");
elem.appendChild(document.createTextNode(msgid + ": " + state));
elem.setAttribute("class", "pending_item "+state);
pending.appendChild(elem);
}
e.appendChild(pending);
// e.appendChild(document.createTextNode(JSON.stringify(j)));
return e;
}
function inject_nntp_feed_element(feed, elem) {
elem.appendChild(document.createElement("hr"));
var name = document.createElement("div");
name.setAttribute("class", "feeds_name");
name_elem = document.createTextNode("Name: "+feed.State.Config.Name);
name.appendChild(name_elem);
elem.appendChild(name);
var conns = document.createElement("div");
conns.setAttribute("class", "connections");
for ( var idx = 0 ; idx < feed.Conns.length; idx ++ ) {
conns.appendChild(createConnectionElement(feed.Conns[idx]));
}
elem.appendChild(conns);
}
function update_nntpchan_feed_ticker(elem) {
nntpchan_admin("feed.list", null, function(j) {
if (j) {
if (j.error) {
console.log("nntpchan_feed_ticker: error, "+j.error);
} else {
// remove all children
while(elem.children.length) {
elem.children[0].remove();
}
var result = j.result;
for (var idx = 0; idx < result.length; idx++) {
var item = result[idx];
var entry = document.createElement("div");
inject_nntp_feed_element(item, entry);
elem.appendChild(entry);
}
}
}
});
}
function nntpchan_mod(mod_action) {
@@ -171,13 +252,7 @@ function nntpchan_mod(mod_action) {
var result = mod_action.handle(j);
if (result) {
elem.appendChild(result);
} else {
// fail
alert("mod action failed, handler returned nothing");
}
} else {
// fail
alert("mod action has no handler");
}
}
} else if (status) {

View File

@@ -24,3 +24,33 @@ function nntpchan_inject_banners(elem, prefix) {
e.id = "nntpchan_banner";
elem.appendChild(e);
}
function get_storage() {
var st = null;
if (window.localStorage) {
st = window.localStorage;
} else if (localStorage) {
st = localStorage;
}
return st;
}
function enable_theme(prefix, name) {
if (prefix && name) {
var theme = document.getElementById("current_theme");
if (theme) {
theme.href = prefix + "static/"+ name + ".css";
var st = get_storage();
st.nntpchan_prefix = prefix;
st.nntpchan_theme = name;
}
}
}
function main() {
// do other initialization here
}
// apply themes
var st = get_storage();
enable_theme(st.nntpchan_prefix, st.nntpchan_theme);

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -1,6 +1,25 @@
body {
background: #EEF2FF url('bg.png') repeat-x 50% 0%;
color: black;
font-family: arial,helvetica,sans-serif;
font-size: 10pt;
margin: 0 4px;
padding-left: 4px;
padding-right: 4px;
}
.frontend , .subject {
color: #928BFF;
main,
aside,
section {
display: block;
margin: 0 auto;
width: 100%;
}
.frontend , .subject, .catalog_subject {
color: #0F0C5D;
}
.name {
@@ -15,13 +34,6 @@
text-align: center;
}
input {
width: 70%;
}
input.button {
width: 25%;
}
textarea {
width: 350px;
@@ -37,33 +49,84 @@ textarea {
pre {
white-space: pre-wrap;
background: #3f3f3f;
color: #d17600;
align: center;
font-size: 13pt;
background: #98E;
color: black;
display: inline-block;
overflow-wrap: break-word;
word-wrap: break-word;
font-weight: bold;
padding: 20px 20px 20px 20px;
margin-left: auto;
margin-right: auto;
}
td {
background-color: #1C1C1C;
table {
margin: auto;
}
table.board-list-table {
width: 100%;
}
table tbody td {
margin: 0;
padding: 4px 15px 4px 4px;
vertical-align: top;
text-align: left;
}
table thead th {
border: 1px solid #000333;
padding: 4px 15px 5px 5px;
background: #98E;
color: #000333;
text-align: left;
white-space: nowrap;
}
table tbody tr:nth-of-type( even ) {
background-color: #D6DAF0;
}
.board_td {
position: relative; left: 2px;
.postform >tr, th {
background: #98E;
font-size: 10pt;
text-align: left;
padding-right: 10px;
padding-left: 4px;
}
.posts_td {
position: relative; left: -2px;
#postform-inner tr {
background-color: transparent!important;
}
#overview_graph {
position: relative;
top: 2px;
background: #1C1C1C;
input[type="text"],input[type="password"],textarea {
border: 1px solid #a9a9a9;
text-indent: 0;
text-shadow: none;
text-transform: none;
word-spacing: normal;
font-size: inherit;
font-family: sans-serif;
}
#board_td {
vertical-align: top;
}
.reply, .pagelist {
background: #D6DAF0;
border-width: 1px;
border-style: none solid solid none;
border-color: #B7C5D9;
display: inline-block;
max-width: 94%!important;
}
.reply, .ukko_thread_header {
background: #202331;
background: #D6DAF0;
margin-left: 24px;
}
legend {
@@ -75,18 +138,79 @@ hr {
border-width: 1px medium medium;
border-color: #b7c5d9;
border-style: solid none none;
clear: both;
}
a:hover , a:visited:hover {
color: #f13333;
.board_header {
font-family: tahoma;
letter-spacing: -2px;
font-size: 20pt;
margin: 0;
color: #AF0A0F;
text-align: center;
font-weight: bold;
margin-top: 2em;
}
a {
color: cornflowerblue;
.board_header_catalog {
text-align: center;
margin-top: 1em;
}
a:visited {
color: #0C2FF3;
.catalog_thread {
display: inline-block;
width: 180px;
text-align: center;
padding: 0.25em;
height: 240px;
overflow: hidden;
background-color: rgba(182, 182, 182 ,0.12);
margin: 0.1em;
border: 2px solid rgba(111, 111, 111, 0.34);
}
.catalog_thread:hover {
background-color: #D6DAF0;
}
#catalog_container {
width: 95%;
margin: 1em auto;
text-align: center;
}
.catalog_header, .catalog_subject {
font-weight: bold;
}
.linkThumb img {
max-height: 128px;
max-width: 128px;
box-shadow: 0 0 4px rgba(0,0,0,0.55);
border: 2px solid rgba(153,153,153,0);
margin-bottom: 5px;
}
a:hover , a:visited:hover, .navbar-link > label:hover {
color: #ff0000;
}
a, a:visited, .navbar-link > label {
color: #34345C;
}
.postno {
font-style: italic;
}
.postno, .postnol, .postnol:visited {
color: black;
text-decoration: none;
}
.postno:hover, .postnol:hover, .postnol:visited:hover {
color: #ff0000;
text-decoration: underline;
}
.navbar {
@@ -96,54 +220,52 @@ a:visited {
right: 0px;
margin-top: 0px;
z-index: 20;
box-shadow: 0px 1px 20px rgba(0, 0, 0, 0.15);
background: #202331;
color: lightgray;
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.15);
border-bottom: 1px solid;
background: #D6DAF0;
color: #34345C;
}
.navbar-sep {
color: darkgray;
}
.navbar-link > label {
text-decoration: underline;
}
#postform_container {
margin-top: 5em;
margin-top: 1em;
}
.navLinks {
padding-bottom: 50px;
}
.ukko_thread_header {
padding: 5px 5px;
margin: 5px 5px;
float: left;
clear: both;
display: inline-block;
}
th {
background: #4DA6BD;
font-size: 10pt;
padding-left: 10px;
padding-right: 15px;
}
input, textarea {
color: whitesmoke;
background: #2d2d2d;
border-color: #4D28CA;
border-radius: 3px;
}
html {
color: #ededed;
background: black;
height: 100%;
font-family: arial,helvetica,sans-serif;
font-size: 10pt;
margin: 0 4px;
padding-left: 4px;
padding-right: 4px;
}
input, textarea {
}
.ukko_thread_header {
border-color: #C0C3D4;
border-top-left-radius: 0px;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
border-bottom-left-radius: 0px;
border-style: dashed;
}
.reply {
margin-left: 1.8em;
display: inline-block;
border-radius: 3px;
}
.frontend {
@@ -152,14 +274,19 @@ html {
}
.op {
margin-right: 20px;
margin-bottom: 10px;
margin-bottom: 1px;
float: left;
clear: both;
}
.post {
display: inline;
margin-bottom: 5px;
min-width: 500px;
}
.postheader {
padding-top: 3px;
padding-right: 5px
}
.tripcode {
@@ -189,12 +316,15 @@ html {
.memearrows {
color: #29D029;
color: #789922;
margin-left: none;
}
.redtext {
color: #d50505;
color: #AF0A0F;
font-size: 12pt;
font-weight: bold;
margin-left: none;
}
.spoiler {
@@ -227,26 +357,55 @@ html {
max-height: 200px;
}
.reply, .ukko_thread_header , pre {
padding: 7px 7px;
border-radius: 5px;
box-shadow: 1px 1px 3px black;
}
.post_body > p {
margin: 0px 0px;
.reply {
padding: 3px 5px 3px 5px;
float: left;
clear: both;
}
.post_body {
display: inline;
margin-left: 15px;
margin-top: 3px;
margin-bottom: 10px;
}
.post_body_line {
margin-left: inherit;
margin-top: 2px;
margin-bottom: 2px;
padding-top: 0px;
padding-bottom: 0px;
}
.post {
display: inline-block;
width: 80%;
overflow: hidden;
}
#postform_attachment {
.pagelist {
display: inline-block;
overflow: hidden;
margin-left: 15px;
margin-top: 20px;
margin-bottom: 50px;
padding: 10px 10px 10px 10px;
font-size: 12pt;
font-weight: medium;
}
.backlink, .backlink:hover, .backlink:visited, .backlink:visited:hover {
margin-left: none;
color: #D00
}
.origin > img{
height: 20px;
vertical-align: text-top;
}
.postform_attachment {
max-width: 330px;
}
@@ -257,35 +416,18 @@ html {
}
#captcha_img {
background: #4DA6BD;
background: white;
}
figure {
float: left;
margin: 1ex;
}
figure img {
max-width:256px;
max-height:128px;
max-width:100%;
}
fieldset {
overflow: auto;
border-radius: 5px 0px 5px 0px;
img.thumbnail {
max-width: 350px;
max-height: 350px;
}
fieldset > img {
float: right;
}
.thread {
float: left;
width: 90%;
padding-left: 10px;
margin-top: 10px;
margin-bottom: 10px;
background-color: #252525;
}
.replybar {
float: left;
@@ -293,9 +435,9 @@ fieldset > img {
top: 2ex;
right: 0;
width: 34%
}
}
@media (max-width: 100ex) {
.replybar, .thread {
.replybar {
width: 100%;
position: static;
}
@@ -318,11 +460,15 @@ textarea#reply-text {
right: 0;
}
hr {
padding-bottom: 20px;
}
#nntpchan_banner {
align: center;
}
.legal {
font-size: 10px;
text-align: center;
}
.attachments {
float: left;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,25 @@
{{!
404.mustache -- 404 page
template parameters:
- prefix (the site prefix)
}}
<html>
<head>
<title> 404 </title>
<meta charset="utf-8" />
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<script type="text/javascript" src="{{prefix}}static/nntpchan.js"></script>
</head>
<body>
<center>
<div class="not_found">
<img src="{{prefix}}static/404.png"></img>
<div class="not_found_message">
{{#i18n.Translations}}{{resource_not_found}}{{/i18n.Translations}}
</div>
</div>
</center>
</body>
</html>

View File

@@ -16,11 +16,11 @@
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<link id="current_theme" rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{board.Board}}</title>
</head>
<body>
<body onload="main()">
<!-- begin navbar -->
{{{board.Navbar}}}
<!-- end navbar -->
@@ -28,6 +28,8 @@
<div id="nntpchan_banner">
</div>
</center>
<div class="board_header">{{board.Board}}</div>
<div class="board_header_catalog"><a href="{{board.Prefix}}catalog-{{board.Name}}.html"> {{#i18n.Translations}}{{catalog_label}}{{/i18n.Translations}} </a></div>
<!-- postform -->
<div id="postform_container">
{{{form}}}
@@ -36,21 +38,32 @@
<div id="threads_container">
{{#board.Threads}}
<div class="thread" id="thread_{{OP.PostHash}}">
<div clsss="thread_header">
<div class="thread_header">
</div>
{{{OP.Truncate.RenderPost}}}
{{#Truncate.Replies}}
{{{Truncate.RenderPost}}}
<br />
{{/Truncate.Replies}}
<hr />
</div>
{{/board.Threads}}
</div>
<div class="pagelist">{{# board.PageList }}[<a href="{{LinkURL}}"> {{Text}} </a>] {{/ board.PageList }}
<span class="navbar-sep">
|
</span>
<a href="{{board.Prefix}}catalog-{{board.Name}}.html"> {{#i18n.Translations}}{{catalog_label}}{{/i18n.Translations}} </a>
</div>
<script type="text/javascript">
var e = document.getElementById("nntpchan_banner");
nntpchan_inject_banners(e, "{{board.Prefix}}");
</script>
<hr/>
<footer>
<p class="legal">All posts on this site are the responsibility of the individual poster and not the administration, pursuant to 47 U.S.C. § 230.</p>
<p class="legal">To make a DMCA request or report illegal content, please contact the administration</p>
</footer>
</body>
</html>

View File

@@ -10,20 +10,22 @@
<head>
<link rel="stylesheet" href="{{prefix}}static/site.css"></link>
<link rel="stylesheet" href="{{prefix}}static/user.css"></link>
<title> {{frontend}} board list </title>
<title> {{frontend}} {{#i18n.Translations}}{{board_list_title}}{{/i18n.Translations}} </title>
</head>
<body>
<center>
<div class="index-outer">
<div class="index-inner">
<table id="board_graph">
<tbody>
<thead>
<tr>
<th> Board </th>
<th> Post per Hour </th>
<th> Post per Day </th>
<th> Total </th>
<th> {{#i18n.Translations}}{{board_label}}{{/i18n.Translations}} </th>
<th> {{#i18n.Translations}}{{pph_label}}{{/i18n.Translations}} </th>
<th> {{#i18n.Translations}}{{ppd_label}}{{/i18n.Translations}} </th>
<th> {{#i18n.Translations}}{{total}}{{/i18n.Translations}} </th>
</tr>
</thead>
<tbody>
{{# graph}}
<tr>
<td>
@@ -46,4 +48,4 @@
</div>
</center>
</body>
</html>
</html>

View File

@@ -0,0 +1,45 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{catalog_title}}{{/i18n.Translations}} {{board.Name}}</title>
</head>
<body>
<!-- begin navbar -->
{{{board.Navbar}}}
<!-- end navbar -->
<center>
<div id="nntpchan_banner">
</div>
</center>
<div class="board_header">{{#i18n.Translations}}{{catalog_title}}{{/i18n.Translations}} {{board.Name}}</div>
<!-- postform -->
<hr />
<div id="catalog_container">
{{#board.Threads}}
<div class="catalog_thread">
<a class="linkThumb" href="{{OP.PostURL}}"><img src="{{OP.RepresentativeThumb}}"></a>
<div class="catalog_header">{{#i18n.Translations}}{{replies_short_label}}{{/i18n.Translations}}: {{ReplyCount}} / {{#i18n.Translations}}{{pictures_short_label}}{{/i18n.Translations}}: {{Page}}</div>
<div class="catalog_subject">{{OP.Subject}}</div>
<div class="catalog_body">
{{{OP.RenderBody}}}
</div>
</div>
{{/board.Threads}}
</div>
<script type="text/javascript">
var e = document.getElementById("nntpchan_banner");
nntpchan_inject_banners(e, "{{board.Prefix}}");
</script>
<hr/>
<footer>
<p class="legal">All posts on this site are the responsibility of the individual poster and not the administration, pursuant to 47 U.S.C. § 230.</p>
<p class="legal">To make a DMCA request or report illegal content, please contact the administration</p>
</footer>
</body>
</html>

View File

@@ -1,9 +1,9 @@
{{!
frontpage.mustache -- template for index.html
template parameters:
- boardgraph ( a boardPageRows instance, see srnd/model.go )
- postgraph ( a postsGraph instance , see srnd/model.go )
- overview ( an overviewModel instance, see srnd/model.go )
- boardgraph ( markup of boardPageRows instance, see srnd/model.go )
- postgraph ( markup of postsGraph instance , see srnd/model.go )
- overview ( markup of overviewModel instance , see srnd/model.go )
- totalposts ( the number of total posts we have ever seen )
- frontend ( the name of the frontend )
- prefix ( the site's prefix )
@@ -12,11 +12,12 @@
<html>
<head>
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link rel="stylesheet" href="{{prefix}}static/user.css" />
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<script src="{{prefix}}static/nntpchan.js" type="text/javascript"></script>
<title> {{frontend}} on nntpchan </title>
</head>
<body>
<body onload="main()">
{{{navbar}}}
<center>
<div class="index-outer">
<div class="index-inner">
@@ -36,18 +37,20 @@
<tbody>
<tr>
<td class="posts_td">
{{{postsgraph.Render}}}
{{{postsgraph}}}
</td>
<td class="board_td">
{{! todo: move boardgraph into its own file like postsgraph }}
<table id="board_graph">
<tbody>
<thead>
<tr>
<th> Board </th>
<th> Posts this Hour </th>
<th> Posts Today </th>
<th> Total </th>
<th> {{#i18n.Translations}}{{board_label}}{{/i18n.Translations}} </th>
<th> {{#i18n.Translations}}{{posts_hour}}{{/i18n.Translations}} </th>
<th> {{#i18n.Translations}}{{posts_today}}{{/i18n.Translations}} </th>
<th> {{#i18n.Translations}}{{total}}{{/i18n.Translations}} </th>
</tr>
</thead>
<tbody>
{{# boardgraph}}
<tr>
<td>
@@ -70,7 +73,7 @@
</tr>
</tbody>
</table>
{{{overview.Render}}}
{{{overview}}}
</div>
</div>
</center>
@@ -78,6 +81,10 @@
var e = document.getElementById("nntpchan_banner");
nntpchan_inject_banners(e, "{{prefix}}");
</script>
<hr/>
<footer>
<p class="legal">All posts on this site are the responsibility of the individual poster and not the administration, pursuant to 47 U.S.C. § 230.</p>
<p class="legal">To make a DMCA request or report illegal content, please contact the administration</p>
</footer>
</body>
</html>

View File

@@ -9,15 +9,15 @@ template parameters:
<meta charset="utf-8"></meta>
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link rel="stylesheet" href="{{prefix}}static/user.css" />
<title> Post History</title>
<title>{{#i18n.Translations}}{{post_history_title}}{{/i18n.Translations}}</title>
</head>
<body>
<td>
<table id="history_graph">
<thead>
<tr>
<th>Month</th>
<th>Posts</th>
<th>{{#i18n.Translations}}{{month}}{{/i18n.Translations}}</th>
<th>{{#i18n.Translations}}{{posts}}{{/i18n.Translations}}</th>
<th></th>
</tr>
</thead>

View File

@@ -10,12 +10,13 @@
<head>
<meta charset="utf-8"></meta>
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link rel="stylesheet" href="{{prefix}}static/user.css" />
<title> here is a new tripcode </title>
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<script type="text/javascript" src="{{prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{new_tripcode_title}}{{/i18n.Translations}}</title>
</head>
<body>
<pre>New Tripcode:
<div id="secret_key">secret: {{secret}}</div><div id="public_key">public: {{public}}</div><div>tripcode: <span class="tripcode" id="capcode_key">{{{tripcode}}}</span></div>
<pre>{{#i18n.Translations}}{{new_tripcode_label}}{{/i18n.Translations}}:
<div id="secret_key">{{#i18n.Translations}}{{secret_key}}{{/i18n.Translations}}: {{secret}}</div><div id="public_key">{{#i18n.Translations}}{{public_key}}{{/i18n.Translations}}: {{public}}</div><div>{{#i18n.Translations}}{{tripcode}}{{/i18n.Translations}}: <span class="tripcode" id="capcode_key">{{{tripcode}}}</span></div>
</pre>
</body>
</html>

View File

@@ -1,14 +1,15 @@
<html>
<head>
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link rel="stylesheet" href="{{prefix}}static/user.css" />
<title>login</title>
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<title>{{#i18n.Translations}}{{login}}{{/i18n.Translations}}</title>
</head>
<body>
<form action="login" method="POST" name="modlogin">
<label for="mod_key">secret</label>
<label for="mod_key">{{#i18n.Translations}}{{secret_key}}{{/i18n.Translations}}</label>
<input type="password" id="mod_key" name="privkey" />
<input type="submit" value="login" />
<input type="submit" value="{{#i18n.Translations}}{{login}}{{/i18n.Translations}}" />
{{{csrfField}}}
</form>
</body>
</html>

View File

@@ -11,9 +11,9 @@
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link rel="stylesheet" href="{{prefix}}static/user.css" />
<meta http-equiv="refresh" content="1; {{mod_prefix}}"></meta>
<title>login</title>
<title>{{#i18n.Translations}}{{login}}{{/i18n.Translations}}</title>
</head>
<body>
<p>{{message}}</p>
</body>
</html>
</html>

View File

@@ -7,96 +7,109 @@
<html>
<head>
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link rel="stylesheet" href="{{prefix}}static/user.css" />
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<!-- yes it uses js -->
<script type="text/javascript" src="{{prefix}}static/nntpchan.js"></script>
<script type="text/javascript" src="{{prefix}}static/mod.js"></script>
<title> nntpchan mod page </title>
<title> {{#i18n.Translations}}{{modpage_title}}{{/i18n.Translations}} </title>
</head>
<body>
<body onload="main()">
<div>
<div> post actions </div>
<div> {{#i18n.Translations}}{{post_actions_label}}{{/i18n.Translations}} </div>
<hr />
<div>
<label for="nntpchan_mod_target">target</label>
<label for="nntpchan_mod_target">{{#i18n.Translations}}{{target_label}}{{/i18n.Translations}}</label>
<input id="nntpchan_mod_target" type="text" />
</div>
<div>
<button onclick="nntpchan_ban()">ban (url)</button>
<button onclick="nntpchan_delete()">delete (url)</button>
<button onclick="nntpchan_unban()">unban (ip)</button>
<button onclick="nntpchan_ban()">{{#i18n.Translations}}{{ban_url_prompt}}{{/i18n.Translations}}</button>
<button onclick="nntpchan_delete()">{{#i18n.Translations}}{{delete_url_prompt}}{{/i18n.Translations}}</button>
<button onclick="nntpchan_unban()">{{#i18n.Translations}}{{unban_ip_prompt}}{{/i18n.Translations}}</button>
</div>
</div>
<hr />
<div>
<div> key actions </div>
<div> {{#i18n.Translations}}{{key_actions_label}}{{/i18n.Translations}} </div>
<div>
<label for="nntpchan_board_target">pubkey:</label>
<label for="nntpchan_board_target">{{#i18n.Translations}}{{pubkey_label}}{{/i18n.Translations}}:</label>
<input type="text" id="nntpchan_key_target" />
</div>
<div>
<button onclick="nntpchan_key_add()">add key</button>
<button onclick="nntpchan_key_del()">remove key</button>
<button onclick="nntpchan_key_add()">{{#i18n.Translations}}{{add_key_prompt}}{{/i18n.Translations}}</button>
<button onclick="nntpchan_key_del()">{{#i18n.Translations}}{{remove_key_prompt}}{{/i18n.Translations}}</button>
</div>
</div>
<hr />
<div>
<div> nntp login </div>
<div> {{#i18n.Translations}}{{nntp_login_label}}{{/i18n.Translations}} </div>
<div>
<label for="nntpchan_nntp_username">username:</label>
<label for="nntpchan_nntp_username">{{#i18n.Translations}}{{username}}{{/i18n.Translations}}:</label>
<input id="nntpchan_nntp_username" />
</div>
<div>
<label for="nntpchan_nntp_passwd">password:</label>
<label for="nntpchan_nntp_passwd">{{#i18n.Translations}}{{password}}{{/i18n.Translations}}:</label>
<input type="password" id="nntpchan_nntp_passwd" />
</div>
<div>
<button onclick="nntpchan_admin_nntp('nntp.login.add')">add user</button>
<button onclick="nntpchan_admin_nntp('nntp.login.add')">{{#i18n.Translations}}{{add_user_prompt}}{{/i18n.Translations}}</button>
</div>
<div>
<button onclick="nntpchan_admin_nntp('nntp.login.del')">remove user</button>
<button onclick="nntpchan_admin_nntp('nntp.login.del')">{{#i18n.Translations}}{{remove_user_prompt}}{{/i18n.Translations}}</button>
</div>
</div>
<hr />
<div>
<div> board actions </div>
<div> {{#i18n.Translations}}{{board_actions_label}}{{/i18n.Translations}} </div>
<div>
<label for="nntpchan_board_target">board name:</label>
<label for="nntpchan_board_target">{{#i18n.Translations}}{{board_name_title}}{{/i18n.Translations}}:</label>
<input id="nntpchan_board_target" />
</div>
<div>
<button onclick="nntpchan_admin_board('frontend.add')">add board</button>
<button onclick="nntpchan_admin_board('frontend.add')">{{#i18n.Translations}}{{add_board_prompt}}{{/i18n.Translations}}</button>
</div>
<div>
<button onclick="nntpchan_admin_board('frontend.regen')">regenerate</button>
<button onclick="nntpchan_admin_board('frontend.regen')">{{#i18n.Translations}}{{regenerate_prompt}}{{/i18n.Translations}}</button>
</div>
<div>
<button onclick="nntpchan_admin_board('frontend.ban')">ban</button>
<button onclick="nntpchan_admin_board('frontend.unban')">unban</button>
<button onclick="nntpchan_admin_board('frontend.ban')">{{#i18n.Translations}}{{ban_prompt}}{{/i18n.Translations}}</button>
<button onclick="nntpchan_admin_board('frontend.unban')">{{#i18n.Translations}}{{unban_prompt}}{{/i18n.Translations}}</button>
</div>
<div>
<button onclick="nntpchan_admin_board('frontend.nuke')">nuke</button>
<button onclick="nntpchan_admin_board('frontend.nuke')">{{#i18n.Translations}}{{nuke_prompt}}{{/i18n.Translations}}</button>
</div>
</div>
<hr />
<div>
<div>
lightweight actions
{{#i18n.Translations}}{{light_actions_label}}{{/i18n.Translations}}
</div>
<div>
<button onclick="nntpchan_admin('template.reload')">reload all templates</button>
<button onclick="nntpchan_admin('template.reload')">{{#i18n.Translations}}{{reload_templates_prompt}}{{/i18n.Translations}}</button>
</div>
</div>
<hr />
<div>
<label>very load heavy actions, use with care</label>
<label>{{#i18n.Translations}}{{heavy_actions_label}}{{/i18n.Translations}}</label>
<div>
<button onclick="nntpchan_admin('frontend.regen')">regenerate all pages</button>
<button onclick="nntpchan_admin('thumbnail.regen')">regenerate all thumbnails</button>
<button onclick="nntpchan_admin('frontend.regen')">{{#i18n.Translations}}{{regenerate_markup_prompt}}{{/i18n.Translations}}</button>
<button onclick="nntpchan_admin('thumbnail.regen')">{{#i18n.Translations}}{{regenerate_thumbs_prompt}}{{/i18n.Translations}}</button>
<button onclick="nntpchan_admin('feed.sync')">{{#i18n.Translations}}{{feed_sync_prompt}}{{/i18n.Translations}}</button>
<button onclick="nntpchan_admin('store.expire')">{{#i18n.Translations}}{{expire_old_prompt}}{{/i18n.Translations}}</button>
</div>
</div>
<div id="nntpchan_mod_result"></div>
<div id="nntpchan_feeds"></div>
<script>
// start nntp feed stats ticker
var e = document.getElementById("nntpchan_feeds");
if (e) {
setInterval(function() {
update_nntpchan_feed_ticker(e);
}, 2000);
}
</script>
<noscript>
<b>enable js to use the mod panel kthx</b>
<b>{{#i18n.Translations}}{{nojs_info}}{{/i18n.Translations}}</b>
</noscript>
</body>
</html>

View File

@@ -12,7 +12,7 @@
<div class="navbar">
<span class="navbar-name">
<span class="board_title">{{name}}</span>
on
{{#i18n.Translations}}{{navbar_on}}{{/i18n.Translations}}
<span class="frontend_title">{{frontend}}</span>
</span>
<span class="navbar-sep">
@@ -31,16 +31,24 @@
|
</span>
<span class="navbar-links">
<span class="navbar-link"><a href="{{prefix}}">front page</a></span>
<span class="navbar-link"><a href="{{prefix}}">{{#i18n.Translations}}{{front_page_title}}{{/i18n.Translations}}</a></span>
</span>
<span class="navbar-sep">
|
</span>
<span class="navbar-links">
<span class="navbar-link"><a href="{{prefix}}ukko.html">overboard</a></span>
<span class="navbar-link"><a href="{{prefix}}ukko.html">{{#i18n.Translations}}{{overboard_title}}{{/i18n.Translations}}</a></span>
</span>
<span class="navbar-sep">
|
</span>
<span class="navbar-links">
<span class="navbar-link"><label onclick="enable_theme('{{prefix}}','bloodgod')">bloodgod</a></span>
<span class="navbar-link"><label onclick="enable_theme('{{prefix}}','dayman')">dayman</a></span>
<span class="navbar-link"><label onclick="enable_theme('{{prefix}}','site')">yotsuba</a></span>
</span>
<span class="navbar-sep">
|
</span>
</div>
<hr />
<hr />

View File

@@ -1,13 +1,13 @@
<html>
<head>
<title>create new board</title>
<title>{{#i18n.Translations}}{{new_board_title}}{{/i18n.Translations}}</title>
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link rel="stylesheet" href="{{prefix}}static/user.css" />
<script type="text/javascript" src="{{prefix}}static/newboard.js">
</script>
</head>
<body>
<p>make your first post</p>
<p>{{#i18n.Translations}}{{first_post_label}}{{/i18n.Translations}}</p>
<hr />
<form action="{{prefix}}post/" enctype="multipart/form-data" name="post" method="post" id="postform" >
<div id="postform-outer">
@@ -16,7 +16,7 @@
<tbody>
<tr>
<th>
Board Name
{{#i18n.Translations}}{{board_name_title}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="name" value="" id="boardname" />
@@ -25,7 +25,7 @@
</tr>
<tr>
<th>
Comment
{{#i18n.Translations}}{{comment}}{{/i18n.Translations}}
</th>
<td>
<textarea id="postform_message" name="message" cols=40 rows=5></textarea>
@@ -33,7 +33,7 @@
</tr>
<tr>
<th>
Captcha
{{#i18n.Translations}}{{captcha}}{{/i18n.Translations}}
</th>
<td>
<img id="captcha_img" src="{{prefix}}captcha/img" alt="captcha" />
@@ -41,7 +41,7 @@
</tr>
<tr>
<th>
Solution
{{#i18n.Translations}}{{solution}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="captcha" />

View File

@@ -8,9 +8,9 @@ paramters:
<table id="overview_graph">
<thead>
<tr>
<th>Newsgroup</th>
<th>Posted</th>
<th>Subject</th>
<th>{{#i18n.Translations}}{{newsgroup}}{{/i18n.Translations}}</th>
<th>{{#i18n.Translations}}{{posted}}{{/i18n.Translations}}</th>
<th>{{#i18n.Translations}}{{subject}}{{/i18n.Translations}}</th>
</tr>
</thead>
<tbody>

View File

@@ -1,26 +1,33 @@
<fieldset>
{{#IsI2P}}
<img src="{{Prefix}}static/i2p.png" title="post from i2p" />
{{/IsI2P}}
{{#IsTor}}
<img src="{{Prefix}}static/tor.png" title="post from tor" />
{{/IsTor}}
{{#IsClearnet}}
<img src="{{Prefix}}static/clearnet.png" title="post from clearnet" />
{{/IsClearnet}}
<legend>
<span class="subject">{{Subject}}</span> <span class="name">{{Name}}</span> {{Date}}
No. <a onclick="nntpchan_backlink('{{ShortHash}}');" title="{{MessageID}}">{{ShortHash}}</a>
<a href="{{PostURL}}">[reply]</a> <span class="tripcode">{{{Pubkey}}}</span><br />
<div id="{{post.PostHash}}" class="{{post.CSSClass}}">
<legend class="postheader">
<span class="origin">
{{#post.IsI2P}}
<img src="{{post.Prefix}}static/i2p.png" title="{{#i18n.Translations}}{{from_i2p}}{{/i18n.Translations}}" />
{{/post.IsI2P}}
{{#post.IsTor}}
<img src="{{post.Prefix}}static/tor.png" title="{{#i18n.Translations}}{{from_tor}}{{/i18n.Translations}}" />
{{/post.IsTor}}
{{#post.IsClearnet}}
<img src="{{post.Prefix}}static/clearnet.png" title="{{#i18n.Translations}}{{from_clearnet}}{{/i18n.Translations}}" />
{{/post.IsClearnet}}
</span>
<span class="subject">{{post.Subject}}</span>
<span class="name">{{post.Name}}</span>
<time datetime="{{post.DateRFC}}">{{post.Date}}</time>
<a href="#" class="postnol">No.</a><a class="postno" onclick="nntpchan_backlink('{{post.ShortHash}}');" title="{{post.MessageID}}">{{post.ShortHash}}</a>
<a href="{{post.PostURL}}">[{{#i18n.Translations}}{{reply_label}}{{/i18n.Translations}}]</a>
<span class="tripcode">{{{post.Pubkey}}}</span>
</legend>
{{#Attachments}}
<div class="attachments">
{{#post.Attachments}}
<figure>
<figcaption>
<a href="{{Source}}" title="{{Filename}}" target="_blank"><img src="{{Thumbnail}}" alt="{{Filename}}" /></a>
<a href="{{Source}}" title="{{Filename}}" target="_blank"><img src="{{Thumbnail}}" alt="{{Filename}}" class="thumbnail"/></a>
</figcaption>
</figure>
{{/Attachments}}
<label class="post_body">
{{{RenderBody}}}
</label>
</fieldset>
{{/post.Attachments}}
</div>
<div class="post_body">
{{{post.RenderBody}}}
</div>
</div>

View File

@@ -1,9 +1,10 @@
<html>
<meta charset="utf-8" />
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link rel="stylesheet" href="{{prefix}}static/user.css" />
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<script type="text/javascript" src="{{prefix}}static/nntpchan.js"></script>
<meta http-equiv="refresh" content="1; {{redirect_url}}" />
<body>
<p>post failed: {{reason}}</p>
<pre>{{#i18n.Translations}}{{post_failed_label}}{{/i18n.Translations}}: {{reason}}</pre>
</body>
</html>
</html>

View File

@@ -24,7 +24,7 @@
<tbody>
<tr>
<th>
Captcha
{{#i18n.Translations}}{{captcha}}{{/i18n.Translations}}
</th>
<td>
<img id="captcha_img" src="{{prefix}}captcha/{{captcha_id}}.png" alt="captcha" />
@@ -32,7 +32,7 @@
</tr>
<tr>
<th>
Solution
{{#i18n.Translations}}{{solution}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="captcha" />
@@ -45,4 +45,4 @@
</div>
</form>
</body>
</html>
</html>

View File

@@ -7,9 +7,10 @@
<html>
<meta charset="utf-8" />
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link rel="stylesheet" href="{{prefix}}static/user.css" />
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<script type="text/javascript" src="{{prefix}}static/nntpchan.js"></script>
<meta http-equiv="refresh" content="2; {{redirect_url}}" />
<body>
<pre>posted as {{message_id}}</pre>
<pre>{{#i18n.Translations}}{{posted_as_label}}{{/i18n.Translations}} {{message_id}}</pre>
</body>
</html>
</html>

View File

@@ -6,8 +6,10 @@
- reference ( the post we are replying to, or empty string if it's an op )
- button ( the text for the reply button )
- files ( bool, do we allow attachments ? )
- csrf ( csrf token )
}}
<form action="{{post_url}}" enctype="multipart/form-data" name="post" method="post">
{{{csrf}}}
<input type="hidden" name="reference" value="{{reference}}" />
<div id="postform-outer">
<div id="postform-inner">
@@ -15,7 +17,7 @@
<tbody>
<tr>
<th>
Name
{{#i18n.Translations}}{{name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="name" value="" />
@@ -23,7 +25,7 @@
</tr>
<tr>
<th>
Subject
{{#i18n.Translations}}{{subject}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="subject" value="" />
@@ -32,25 +34,25 @@
</tr>
<tr>
<th>
Comment
{{#i18n.Translations}}{{comment}}{{/i18n.Translations}}
</th>
<td>
<textarea id="postform_message" name="message" cols=40 rows=5></textarea>
</td>
</tr>
{{#files}}
<tr>
<tr>
<th>
File
{{#i18n.Translations}}{{file}}{{/i18n.Translations}}
</th>
<td>
<input id="postform_attachment" type="file" name="attachment" />
<input class="postform_attachment" id="postform_attachments" type="file" name="attachment_uploaded" multiple />
</td>
</tr>
{{/files}}
<tr>
<th>
Get Dubs
{{#i18n.Translations}}{{dubs}}{{/i18n.Translations}}
</th>
<td>
<input type="checkbox" name="dubs" />
@@ -58,7 +60,7 @@
</tr>
<tr>
<th>
Captcha
{{#i18n.Translations}}{{captcha}}{{/i18n.Translations}}
</th>
<td>
<img id="captcha_img" src="{{prefix}}captcha/img" alt="captcha" />
@@ -66,10 +68,10 @@
</tr>
<tr>
<th>
Solution
{{#i18n.Translations}}{{solution}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="captcha" />
<input type="text" name="captcha" autocomplete="off" />
</td>
</tr>
</tbody>

View File

@@ -8,8 +8,8 @@ parameters:
<table id="posts_graph">
<thead>
<tr>
<th>Day</th>
<th>Posts</th>
<th>{{#i18n.Translations}}{{day}}{{/i18n.Translations}}</th>
<th>{{#i18n.Translations}}{{posts}}{{/i18n.Translations}}</th>
<th></th>
</tr>
</thead>

View File

@@ -3,7 +3,6 @@
template parameters:
- board ( the Board Model of the board this thread was posted in )
- thread ( the Thread Model of the current thread being rendered )
- form ( the post form markup )
Thread Model attributes:
- OP , the Post Model of the original poster
@@ -16,12 +15,11 @@
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{thread.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{thread.Prefix}}static/user.css" />
<link id="current_theme" rel="stylesheet" href="{{thread.Prefix}}static/user.css" />
<script src="{{thread.Prefix}}static/nntpchan.js" type="text/javascript"></script>
<title> {{thread.OP.Subject}} </title>
</head>
<body>
<body onload="main()">
<!-- begin navbar -->
{{{thread.Navbar}}}
<!-- end navbar -->
@@ -29,6 +27,7 @@
<div id="nntpchan_banner">
</div>
</center>
<div class="board_header">{{thread.Board}}</div>
<!-- postform -->
<div id="postform_container">
{{{form}}}
@@ -43,12 +42,17 @@
<br/>
{{/ thread.Replies}}
</div>
<br/>
<hr/>
<div class="navLinks">[<a href="{{thread.BoardURL}}" accesskey="a">{{#i18n.Translations}}{{return}}{{/i18n.Translations}}</a>] [<a href="{{thread.Prefix}}catalog-{{thread.Board}}.html" accesskey="r">{{#i18n.Translations}}{{catalog_label}}{{/i18n.Translations}}</a>] [<a href="#thread_{{thread.OP.PostHash}}">{{#i18n.Translations}}{{to_top_label}}{{/i18n.Translations}}</a>] [<a href="#navlinks" accesskey="r" onclick="location.reload();">{{#i18n.Translations}}{{update}}{{/i18n.Translations}}</a>]</div>
<script type="text/javascript">
var e = document.getElementById("nntpchan_banner");
nntpchan_inject_banners(e, "{{thread.Prefix}}");
</script>
<hr/>
<footer>
<p class="legal">All posts on this site are the responsibility of the individual poster and not the administration, pursuant to 47 U.S.C. § 230.</p>
<p class="legal">To make a DMCA request or report illegal content, please contact the administration</p>
</footer>
</body>
</html>
</html>

View File

@@ -6,32 +6,32 @@
}}
<html>
<head>
<title> ukko / overboard </title>
<title> {{#i18n.Translations}}{{overboard_title}}{{/i18n.Translations}} </title>
<meta charset="utf-8" />
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link rel="stylesheet" href="{{prefix}}static/user.css" />
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<script type="text/javascript" src="{{prefix}}static/nntpchan.js"></script>
</head>
<body>
<body onload="main()">
<center>
<div id="nntpchan_banner">
</div>
</center>
<!-- begin navbar -->
{{{navbar}}}
<!-- end navbar -->
<div class="board_header"> {{#i18n.Translations}}{{overboard_title}}{{/i18n.Translations}} </div>
<div id="ukko_threads">
{{#threads}}
<!--
<div class="ukko_thread_header">
<p> Posted on <a href="{{{BoardURL}}}"><span class="ukko_boardname">{{OP.Board}}</span></a></p>
</div>
-->
<div class="thread" id="thread_{{OP.PostHash}}">
<div clsss="thread_header">
</div>
<div class="ukko_thread_header">
<p>{{#i18n.Translations}}{{posted_on_label}}{{/i18n.Translations}} <a href="{{{BoardURL}}}"><span class="ukko_boardname">{{OP.Board}}</span></a></p>
</div>
{{{OP.Truncate.RenderPost}}}
{{#Truncate.Replies}}
{{{Truncate.RenderPost}}}
<br />
{{/Truncate.Replies}}
<hr />
</div>
{{/threads}}
</div>
@@ -40,5 +40,10 @@
nntpchan_inject_banners(e, "{{prefix}}");
</script>
<hr/>
<footer>
<p class="legal">All posts on this site are the responsibility of the individual poster and not the administration, pursuant to 47 U.S.C. § 230.</p>
<p class="legal">To make a DMCA request or report illegal content, please contact the administration</p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,51 @@
var http = require('http');
var makeIpBans = function(cidrs, privkey, cb) {
cb({
message: cidrs.join("\noverchan-inet-ban "),
name: "mod#"+privkey,
frontend: "memegod.censor",
newsgroup: "ctl"
})
}
var makeDeletePosts = function(msgids, privkey, cb) {
cb({
message: msgids.join("\ndelete "),
name: "mod#"+privkey,
frontend: "memegod.censor",
newsgroup: "ctl",
})
}
var moderate = function(req) {
j = JSON.stringify(req);
var r = http.request({
port: 8800,
method: "POST",
path: "/api/post",
auth: "user:pass",
headers: {
"Content-Type": "text/json",
"Content-Length": j.length
}
}, function (res) {
res.on('data', function (chunk) {
var r = chunk.toString();
var rj = JSON.parse(r);
console.log(rj.id);
});
});
r.write(j);
r.end();
}
var privateKey = "longhexgoestripcodegoeshere";
// ban 192.168.0.1/16 and sign with private key
makeIpBans(["192.168.0.1/16"], privateKey, moderate);
// delete <msg1@place.tld> and <msg2@otherplace.tld> and sign with private key
makeDeletPosts(["<msg1@place.tld>", "<msg2@otherplace.tld>"], privateKey, moderate);

View File

@@ -75,6 +75,28 @@ class Article:
if msg:
return "{}\n{}".format(self.header(), msg)
def getAttachmentPart(self, j):
msg = ''
mtype = 'image'
if j['ext'] in ['.mp4', '.webm']:
mtype = 'video'
url = 'https://{}/{}/src/{}{}'.format(self.site, self.board, j['tim'], j['ext'])
print ('obtain {}'.format(url))
r = requests.get(url)
if r.status_code == 200:
msg += '--{}\n'.format(self.boundary)
msg += 'Content-Type: {}/{}\n'.format(mtype, j['ext'])
msg += 'Content-Disposition: form-data; filename="{}{}"; name="import"\n'.format(j['filename'], j['ext'])
msg += 'Content-Transfer-Encoding: base64\n'
msg += '\n'
msg += base64.b64encode(r.content).decode('ascii')
msg += '\n'
else:
print ('failed to obtain attachment: {} != 200'.format(r.status_code))
return msg
def bodyMultipart(self):
self.boundary = '========{}'.format(random.randint(0, 10000000))
msg = self.header() + '\n'
@@ -82,24 +104,10 @@ class Article:
msg += 'Content-Type: text/plain; encoding=UTF-8\n'
msg += '\n'
msg += self.message() + '\n'
msg += '--{}\n'.format(self.boundary)
mtype = 'image'
if self.j['ext'] in ['.mp4', '.webm']:
mtype = 'video'
msg += 'Content-Type: {}/{}\n'.format(mtype, self.j['ext'])
msg += 'Content-Disposition: form-data; filename="{}{}"; name="import"\n'.format(self.j['filename'], self.j['ext'])
msg += 'Content-Transfer-Encoding: base64\n'
msg += '\n'
url = 'https://{}/{}/src/{}{}'.format(self.site, self.board, self.j['tim'], self.j['ext'])
print ('obtain {}'.format(url))
r = requests.get(url)
if r.status_code == 200:
msg += base64.b64encode(r.content).decode('ascii')
msg += '\n'
else:
print ('failed to obtain attachment: {} != 200'.format(r.status_code))
return
msg += self.getAttachmentPart(self.j)
if 'extra_files' in self.j:
for j in self.j['extra_files']:
msg += self.getAttachmentPart(j)
msg += '\n--{}--\n'.format(self.boundary)
return msg
@@ -111,8 +119,9 @@ class Article:
class Poster:
def __init__(self, host, port):
def __init__(self, host, port, user, passwd):
self.host, self.port = host, port
self.user, self.passwd = user, passwd
def post(self, articles):
"""
@@ -121,7 +130,7 @@ class Poster:
if isinstance(articles, Article):
return self.post([articles])
else:
n = nntplib.NNTP(self.host, self.port)
n = nntplib.NNTP(self.host, self.port, self.user, self.passwd)
for article in articles:
body = article.body()
if body:
@@ -144,7 +153,7 @@ class Getter:
self.site = url_parse(url).hostname
self.board = url_parse(url).path.split('/')[1]
def get(self):
def get(self, thread=False):
"""
yield a bunch of articles
"""
@@ -155,12 +164,17 @@ class Getter:
except:
pass
else:
if 'threads' in j:
for t in j['threads']:
posts = t['posts']
for post in posts:
if thread:
if 'posts' in j:
for post in j['posts']:
yield Article(post, self.board, self.site)
else:
if 'threads' in j:
for t in j['threads']:
posts = t['posts']
for post in posts:
yield Article(post, self.board, self.site)
def main():
@@ -169,11 +183,21 @@ def main():
ap.add_argument('--server', type=str, required=True)
ap.add_argument('--port', type=int, required=True)
ap.add_argument('--board', type=str, required=True)
ap.add_argument('--thread', type=str, required=False)
ap.add_argument('--user', type=str, required=True)
ap.add_argument('--passwd', type=str, requried=True)
args = ap.parse_args()
poster = Poster(args.server, args.port)
for n in range(10):
getter = Getter('https://8ch.net/{}/{}.json'.format(args.board, n))
poster.post(getter.get())
poster = Poster(args.server, args.port, args.user, args.passwd)
if args.thread:
# only archive 1 thread
getter = Getter('https://8ch.net/{}/res/{}.json'.format(args.board, thread))
poster.post(getter.get(thread=True))
else:
# archive the entire board
for n in range(10):
getter = Getter('https://8ch.net/{}/{}.json'.format(args.board, n))
poster.post(getter.get())
if __name__ == "__main__":

View File

@@ -0,0 +1,209 @@
#!/usr/bin/env nodejs
var memegod = require("mongodb");
var memegodClient = memegod.MongoClient;
var assert = require("assert");
var http = require("http");
// memegod daemon url
var url = "mongodb://127.0.0.1:27017/lynxchan";
// nntp frontend name for article import
var frontendName = "memegod.import";
// srndv2 http frontend server to do api requests against
var srndApiServ = "localhost";
var srndApiPort = 18000;
// username password combo for using api
var srndApiLogin = "user:pass";
// some memegod thing
var idPrefix="v1-";
// create a newsgroup name given board name
var makeGroupName = function(board) {
return "overchan.test.endchan." + board.boardUri;
}
// call function for each op on a board
var foreachOpsOnBoard = function(db, board, callback, done) {
var cur = db.collection("threads").find({boardUri: board});
var ops = [];
cur.each(function(err, doc) {
if (doc) {
ops.push(doc)
} else {
for (var idx = 0 ; ops.length > idx ; idx ++ ) {
callback(ops[idx]);
}
if(done)
done();
}
});
}
// call a callback for each reply to op
// pass in the memegod post
var foreachReplyForOP = function(db, op, callback, done) {
var doit = function(op, msgid) {// we don't has got it
var cur = db.collection("posts").find({ threadId: op.threadId});
var repls = [];
cur.each(function(err, doc) {
if (doc) {
repls.push(doc)
} else {
for (var idx = 0 ; repls.length > idx ; idx ++ ) {
callback(repls[idx]);
}
if(done)
done();
}
});
}
checkPostExists(op, doit, doit);
}
// find all boards in memegod
// call callback for each board
var foreachBoard = function(db, callback, done) {
var cursor = db.collection('boards').find();
var boards = [];
cursor.each(function(err, doc) {
if (doc) {
boards.push(doc)
} else {
for (var idx = 0 ; boards.length > idx ; idx ++ ) {
callback(boards[idx]);
}
if(done)
done();
}
});
};
// convert a memegod post from board into an overchan article
// call a callback with the created post
var createArticle = function(post, board, callback) {
if (post == null) {
callback(null);
return
}
var article = {
ip: post.ip.join("."),
message: post.message || " ",
subject: post.subject || "MongoDB is web scale",
frontend: frontendName,
newsgroup: makeGroupName(board),
headers: {
}
};
if (post.postId) {
article.headers["X-Memegod-Post-Id"] = idPrefix+post.postId
}
if (post.threadId) {
article.headers["X-Memegod-Thread-Id"] = idPrefix+post.threadId;
}
article.headers["X-Memegod-Id"] = idPrefix+post._id;
article.headers["X-Migrated-From"] = "MemeGod";
article.name = post.name || "Stephen Lynx";
callback(article);
}
// post an overchan article via the api
// call callback passing in the message-id of the new post
var postArticle = function(article, callback) {
if (article == null) { callback(null); return; }
checkViaHeader("X-Memegod-Id", article.headers["X-Memegod-Id"], function(msgid) {
// we has got it already
callback(msgid);
}, function(msgid) {
// we don't has got it
var req = http.request({
port: srndApiPort,
method: "POST",
path: "/api/post",
auth: srndApiLogin,
headers: {
"Content-Type": "text/json",
}
}, function(res) {
var data = "";
res.on("data", function (chunk) {
data += chunk;
});
res.on("end", function() {
var j = JSON.parse(data)
var msgid = j.id;
callback(msgid);
})
});
req.write(JSON.stringify(article));
req.end();
});
}
// check if an article exists given header name and header value
var checkViaHeader = function(name, value, yesCb, noCb) {
var req = http.request({
port: srndApiPort,
method: "GET",
path: "/api/header?name="+name+"&value="+value,
}, function (res) {
var data = "";
res.on("data", function(chnk) {
data += chnk;
});
res.on("end", function() {
var j = JSON.parse(data);
if ( j.length > 0 ) {
// it exists
yesCb(j[0]);
} else {
// does not exist
noCb();
}
});
});
req.end();
}
// check if a post exists
var checkPostExists = function(post, yescb, nocb) {
checkViaHeader("X-Memegod-Id", idPrefix + post._id,
function(msgid) { yescb(post, msgid); },
function() { nocb(post); });
}
var putBoard = function(db, board, done) {
foreachOpsOnBoard(db, board.boardUri, function(originalPost) {
var doit = function(op) {
createArticle(op, board, function(opArticle) {
postArticle(opArticle, function(opMsgId) {
// for each reply for OP
foreachReplyForOP(db, op, function(post) {
checkPostExists(post , function(msgid) {
// we have this post
}, function(post, msgid) {
// put create reply
createArticle(post, board, function(article) {
// set references header
article.headers["References"] = opMsgId;
postArticle(article, function(msgid) {});
});
});
});
});
});
}
checkPostExists(originalPost, doit, doit);
});
}
memegodClient.connect(url, function(err, db) {
console.log("connected to the meme god");
foreachBoard(db, function(board) {
console.log("updating "+board.boardUri);
putBoard(db, board);
});
});

View File

@@ -0,0 +1,117 @@
[formats]
full_date_format=15:04:05 02.01.2006
month_date_format=01.2006
day_date_format=02.01
[strings]
#catalog
catalog_title=Katalog für
replies_short_label=A
pictures_short_label=B
#board
catalog_label=Katalog
#board list
board_list_title=Boardliste
board_label=Board
pph_label=Posts pro Stunde
ppd_label=Posts pro Tag
total=Gesamt
#front page
posts_today=Posts heute
posts_hour=Posts diese Stunde
#history
month=Monat
posts=Posts
post_history_title=Postverlauf
#keygen
new_tripcode_title=Hier ist der neue Tripcode
new_tripcode_label=Neuer Tripcode
secret_key=Privater Schlüssel
public_key=Öffentlicher Schlüssel
tripcode=Tripcode
#login
login=Anmeldung
#modpage
modpage_title=nntpchan-Moderationsseite
post_actions_label=Postfunktionen
target_label=Ziel
ban_url_prompt=Bannen (URL)
delete_url_prompt=Löschen (URL)
unban_ip_prompt=Bann aufheben (IP)
key_actions_label=Schlüsselfunktionen
pubkey_label=Öffentlicher Schlüssel
add_key_prompt=Schlüssel hinzufügen
remove_key_prompt=Schlüssen entfernen
nntp_login_label=NNTP-Benutzer
username=Benutzername
password=Password
add_user_prompt=Benutzer hinzufügen
remove_user_prompt=Benutzer entfernen
board_actions_label=Boardfunktionen
board_name_title=Boardname
add_board_prompt=Board hinzufügen
regenerate_prompt=Regenerieren
ban_prompt=Bannen
unban_prompt=Bann aufheben
nuke_prompt=Sprengen
light_actions_label=Schnelle Funktionen
reload_templates_prompt=Alle Templates erneut laden
heavy_actions_label=Rechenintensive Funktionen (mit Vorsicht verwenden)
regenerate_markup_prompt=Alle HTML-Seiten regenerieren
regenerate_thumbs_prompt=Alle Vorschaubilder regenerieren
nojs_info=Bitte aktivieren Sie JavaScript, um diese Seite zu nutzen.
#navbar
navbar_on=@
front_page_title=Startseite
overboard_label=Überboard
#newboard
new_board_title=Neues Board erstellen
first_post_label=Machen Sie den ersten Post
comment=Kommentar
captcha=Captcha
solution=Lösung
#overview
newsgroup=Newsgroup
posted=Gepostet
subject=Betreff
#post
from_i2p=Vom I2P-Netzwerk gepostet
from_tor=Vom Tor-Netzwerk gepostet
from_clearnet=Vom Clearnet gepostet
reply_label=Antworten
#post failed
post_failed_label=Post fehlgeschlagen
#post form
name=Name
file=Anhang
dubs=Doppel?
#posts graph
day=Tag
#post success
posted_as_label=Gepostet als
#thread
return=Zurück
to_top_label=Nach oben
update=Updaten
#ukko
overboard_title=ukko / Überboard
posted_on_label=Gepostet in

View File

@@ -0,0 +1,121 @@
[formats]
full_date_format=Mon Jan _2 15:04:05 2006
month_date_format=2006-02
day_date_format=01-02
[strings]
#catalog
catalog_title=Catalog for
replies_short_label=R
pictures_short_label=P
#board
catalog_label=Catalog
#board list
board_list_title=board list
board_label=Board
pph_label=Posts per Hour
ppd_label=Posts per Day
total=Total
#front page
posts_today=Posts Today
posts_hour=Posts this Hour
#history
month=Month
posts=Posts
post_history_title=Post History
#keygen
new_tripcode_title=here is a new tripcode
new_tripcode_label=New Tripcode
secret_key=secret
public_key=public
tripcode=tripcode
#login
login=login
#modpage
modpage_title=nntpchan mod page
post_actions_label=post actions
target_label=target
ban_url_prompt=ban (url)
delete_url_prompt=delete (url)
unban_ip_prompt=unban (ip)
key_actions_label=key actions
pubkey_label=pubkey
add_key_prompt=add key
remove_key_prompt=remove key
nntp_login_label=nntp login
username=username
password=password
add_user_prompt=add user
remove_user_prompt=remove user
board_actions_label=board actions
board_name_title=board name
add_board_prompt=add board
regenerate_prompt=regenerate
ban_prompt=ban
unban_prompt=unban
nuke_prompt=nuke
light_actions_label=lightweight actions
reload_templates_prompt=reload all templates
heavy_actions_label=very load heavy actions, use with care
regenerate_markup_prompt=regenerate all markup
regenerate_thumbs_prompt=regenerate all thumbnails
feed_sync_prompt=manually synchronize all articles
expire_old_prompt=manually expire orphaned articles
nojs_info=enable js to use the mod panel kthx
#navbar
navbar_on=on
front_page_title=front page
overboard_title=overboard
#newboard
new_board_title=create new board
first_post_label=make your first post
comment=Comment
captcha=Captcha
solution=Solution
#overview
newsgroup=Newsgroup
posted=Posted
subject=Subject
#post
from_i2p=posted from i2p
from_tor=posted from tor
from_clearnet=posted from clearnet
reply_label=Reply
#post failed
post_failed_label=post failed
#post form
name=Name
file=File
dubs=Get Dubs
#posts graph
day=Day
#post success
posted_as_label=posted as
#thread
return=Return
to_top_label=Top
update=Update
#ukko
overboard_title=ukko / overboard
posted_on_label=Posted on
#404 page
resource_not_found=The page you are looking for isn't here

View File

@@ -0,0 +1,117 @@
[formats]
full_date_format=Lunes 2_Enero 15:04:05 2006
month_date_format=02-2006
day_date_format=01-02
[strings]
#catalog
catalog_title=Catálogo para
replies_short_label=R
pictures_short_label=P
#board
catalog_label=Catálogo
#board list
board_list_title=Lista de canales
board_label=Canales
pph_label=Envíos a la hora
ppd_label=Envíos al Día
total=Total
#front page
posts_today=Envíos de hoy
posts_hour=Envíos de esta última hora
#history
month=Mes
posts=Envíos
post_history_title=Historial de envíos
#keygen
new_tripcode_title=Esto es un nuevo tripcode
new_tripcode_label=Nuevo Tripcode
secret_key=secreto
public_key=público
tripcode=tripcode
#login
login=inicio de sesión
#modpage
modpage_title=página configuración nntpchan
post_actions_label=acciones de envíos
target_label=objetivo
ban_url_prompt=prohibir (url)
delete_url_prompt=borrar (url)
unban_ip_prompt=permitir (ip)
key_actions_label=opciones de claves
pubkey_label=pubkey
add_key_prompt=añadir clave
remove_key_prompt=eliminar clave
nntp_login_label=inicio de sesión nntp
username=nombre de usuario
password=contraseña
add_user_prompt=añadir usuario
remove_user_prompt=eliminar usuario
board_actions_label=acciones sobre los canales
board_name_title=nombre del canal
add_board_prompt=añadir canal
regenerate_prompt=regenerar
ban_prompt=prohibir
unban_prompt=permitir
nuke_prompt=destruir
light_actions_label=acciones sencillas
reload_templates_prompt=recargar todas las plantillas
heavy_actions_label=acciones que consumen muchos recursos, usar con cuidado
regenerate_markup_prompt=regenerar todos los formatos
regenerate_thumbs_prompt=regenerar todas las miniaturas
nojs_info=habilitar JS para utilizar el panel de mods kthx
#navbar
navbar_on=on
front_page_title=página principal
overboard_title=overboard
#newboard
new_board_title=crear un nuevo canal
first_post_label=crear tu primer envío
comment=Comentario
captcha=Captcha
solution=Solución
#overview
newsgroup=Grupo de noticias
posted=Enviado
subject=Asunto
#post
from_i2p=enviado desde i2p
from_tor=enviado desde tor
from_clearnet=enviado desde clearnet
reply_label=Responder
#post failed
post_failed_label=el envío a fallado
#post form
name=Nombre
file=Archivo
dubs=Obtener 'Dubs' (dobles)
#posts graph
day=Día
#post success
posted_as_label=enviado como
#thread
return=Volver
to_top_label=Arriba
update=Actualizar
#ukko
overboard_title=ukko / overboard
posted_on_label=Enviado en

View File

@@ -0,0 +1,116 @@
[formats]
full_date_format=Segunda, _2 de Janeiro 15:04:05 2006
month_date_format=20-2006
day_date_format=01-02
[strings]
#catalog
catalog_title=Catálogo para
replies_short_label=R
pictures_short_label=I
#board
catalog_label=Menu
#board list
board_list_title=lista de canais
board_label=Canais
pph_label=Envios por hora
ppd_label=Envios por dia
total=Total
#front page
posts_today=Envios hoje
posts_hour=Envios na ultima hora
#history
month=Mês
posts=Envios
post_history_title=Histórico
#keygen
new_tripcode_title=Isto é um novo tripcode
new_tripcode_label=Novo tripcode
secret_key=segredo
public_key=público
tripcode=tripcode
#login
login=login
#modpage
modpage_title=Página de configuração do nntpchan
post_actions_label=ações dos envios
target_label=objetivo
ban_url_prompt=banir (url)
delete_url_prompt=deletar (url)
unban_ip_prompt=permitir (ip)
key_actions_label=opções de teclas
pubkey_label=pubkey
add_key_prompt=adicionar chave
remove_key_prompt=remover chave
nntp_login_label=iniciar sessão nntp
username=nome de usuário
password=senha
add_user_prompt=adicionar usuário
remove_user_prompt=remover usuário
board_actions_label=ações de canais
board_name_title=nome do canal
add_board_prompt=adicionar canal
regenerate_prompt=gerar novamente
ban_prompt=banir
unban_prompt=permitir
nuke_prompt=destruir
light_actions_label=ações leves
reload_templates_prompt=recarregar todos os padrões
heavy_actions_label=ações que consumem muitos recursos, usar com cuidado
regenerate_markup_prompt=recarregar todos os formatos
regenerate_thumbs_prompt=recarregar todas as miniaturas
nojs_info=habilitar JS para usar o painel de mods kthx
#navbar
navbar_on=on
front_page_title=página inicial
overboard_title=overboard
#newboard
new_board_title=criar um novo canal
first_post_label=fazer seu primeiro envio
comment=Comentário
captcha=Captcha
solution=Solução
#overview
newsgroup=Grupo de notícias
posted=Enviado
subject=Assunto
#post
from_i2p=enviado da i2p
from_tor=enviado da tor
from_clearnet=enviado da clearnet
reply_label=Responder
#post failed
post_failed_label=o envio falhou
#post form
name=Nome
file=Arquivo
dubs=Obter "Dubs" (pares)
#posts graph
day=Dia
#post success
posted_as_label=enviado como
#thread
return=Voltar
to_top_label=Acima
update=Atualizar
#ukko
overboard_title=ukko / overboard
posted_on_label=Enviado em

View File

@@ -0,0 +1,116 @@
[formats]
full_date_format=_2 Янв 2006 г. Пн 15:04:05
month_date_format=01.2006
day_date_format=02.01
[strings]
#catalog
catalog_title=Каталог
replies_short_label=О
pictures_short_label=И
#board
catalog_label=Каталог
#board list
board_list_title=Список досок
board_label=Доска
pph_label=Постов в час
ppd_label=Постов в день
total=Всего
#front page
posts_today=Постов за сегодня
posts_hour=Постов за этот час
#history
month=Месяц
posts=Посты
post_history_title=История поста
#keygen
new_tripcode_title=вот новый трипкод
new_tripcode_label=Новый трипкод
secret_key=закрытый
public_key=открытый
tripcode=трипкод
#login
login=войти
#modpage
modpage_title=страница модерации nntpchan
post_actions_label=опции поста
target_label=цель
ban_url_prompt=забанить (url)
delete_url_prompt=удалить (url)
unban_ip_prompt=разбанить (ip)
key_actions_label=опции ключа
pubkey_label=открытый ключ
add_key_prompt=добавить ключ
remove_key_prompt=удалить ключ
nntp_login_label=nntp авторизация
username=имя пользователя
password=пароль
add_user_prompt=добавить пользователя
remove_user_prompt=удалить пользователя
board_actions_label=опции доски
board_name_title=название доски
add_board_prompt=добавить доску
regenerate_prompt=восстановить
ban_prompt=забанить
unban_prompt=разбанить
nuke_prompt=уничтожить
light_actions_label=малопотребляющие опции
reload_templates_prompt=перезагрузить все шаблоны
heavy_actions_label=многопотребляющие опции (использовать с осторожностью)
regenerate_markup_prompt=восстановить всю разметку
regenerate_thumbs_prompt=восстановить все миниатюры
nojs_info=включи JS чтобы пользоваться панелью модерации ок спс
#navbar
navbar_on=вкл
front_page_title=главная страница
overboard_title=сверхдоска
#newboard
new_board_title=создать новую доску
first_post_label=сделай свой первый пост
comment=Комментарий
captcha=Капча
solution=Ответ
#overview
newsgroup=Группа новостей
posted=Отправлено
subject=Тема
#post
from_i2p=отправлено через i2p
from_tor=отправлено через tor
from_clearnet=отправлено через чистонет
reply_label=Ответить
#post failed
post_failed_label=отправка не удалась
#post form
name=Имя
file=Файл
dubs=Получить Дабл
#posts graph
day=День
#post success
posted_as_label=отправлено как
#thread
return=Вернуться
to_top_label=К началу
update=Обновить
#ukko
overboard_title=ukko / сверхдоска
posted_on_label=Опубликованно в

View File

@@ -4,12 +4,17 @@
## requirements ##
* linux or freebsd
* go 1.3 or higher
* libsodium 1.0 or higher
* imagemagick
* ffmpegthumbnailer
* sox
## supported go versions ##
* `go 1.6` or higher with redis driver
* `go 1.3` or higher without redis driver
## debian ##
@@ -25,4 +30,8 @@ Check out the repo and build it
cd nntpchan
./build.sh
If you want to build without supporting redis then build with the `--no-redis` flag
./build.sh --no-redis
Now configure the database. [next](database.md)

View File

@@ -1,28 +1,5 @@
You have 2 choices for database backend:
Postgres on Debian (as root)
* [postgres](postgres.md)
# install as root
apt-get install --no-install-recommends postgresql postgresql-client
Setting up postgres (as root)
# become postgres user
su postgres
# spawn postgres admin shell
psql
You'll get a prompt, enter the following:
CREATE ROLE srnd WITH LOGIN PASSWORD 'srnd';
CREATE DATABASE srnd WITH ENCODING 'UTF8' OWNER srnd;
\q
For demo purposes we'll use these credentials.
These are default values, please change them later.
## important
these credentials assume you are going to run using a user called `srnd`, if your username you plan to run the daemon as is different please change `srnd` to your username.
Now run it, [next](running.md)
* [redis](redis.md)

28
doc/postgres.md Normal file
View File

@@ -0,0 +1,28 @@
Postgres on Debian (as root)
# install as root
apt-get install --no-install-recommends postgresql postgresql-client
Setting up postgres (as root)
# become postgres user
su postgres
# spawn postgres admin shell
psql
You'll get a prompt, enter the following:
CREATE ROLE srnd WITH LOGIN PASSWORD 'srnd';
CREATE DATABASE srnd WITH ENCODING 'UTF8' OWNER srnd;
\q
For demo purposes we'll use these credentials.
These are default values, please change them later.
## important
these credentials assume you are going to run using a user called `srnd`, if your username you plan to run the daemon as is different please change `srnd` to your username.
Now run it, [next](running.md)

View File

@@ -314,6 +314,47 @@ The following part is signed:
Above example in octets:
Content-Type: text/plain; charset=UTF-8\\r\\nDate: Thu, 02 May 2013 12:16:44 +0000\\r\\n\\r\\ndelete-x-all <message-ID>\\r\\ndelete <message-ID>\\r\\n\\r\\ndelete <message-ID>
## RPC
Remote procedure calls can be sent via ``ctl`` or on a group basis by using the group ``ctl.overchan.*``, where * is the group for which you want to execute a certain operation.
### Default format
The default format uses the MIME type ``text/plain`` where the first line of the body opens an array with ``[`` the next line is the name of the procedure you want to call, and on the lines following you can add one or more parameters. Each of these lines is terminated with `,` and indention can be added as well. The arry is closed with `]`.
#### Exapmple
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
From: anonymous <foo@bar.ano>
Date: Thu, 02 May 2013 12:16:44 +0000
Message-ID: <h2cykk1lwlmuqao2qiy@foo.bar>
Newsgroups: ctl.overchan.foo
Subject: RPC
Path: hschan.ano
[
setSetting,
bumplimit,
350,
]
### JSON RPC
If the MIME type is specified as ``application/json`` the body is interpreted as [JSON RPC](http://json-rpc.org/).
#### Exapmple
Content-Type: application/json; charset=UTF-8
Content-Transfer-Encoding: 8bit
From: anonymous <foo@bar.ano>
Date: Thu, 02 May 2013 12:16:44 +0000
Message-ID: <h2cykk1lwlmuqao2qiy@foo.bar>
Newsgroups: ctl.overchan.foo
Subject: RPC
Path: hschan.ano
{"method": "setSetting", "params": ["bumplimit", "350"], "id": null}
### Additional details
As described above, muliple RPC's can be sent via the multipart format. It is also expected that these articles are signed.
# Glossary

37
doc/redis.md Normal file
View File

@@ -0,0 +1,37 @@
# configuring redis database backend
0) Install redis
Redis 3.x or higher is required
0.A) debian/ubuntu
# apt update
# apt install redis-server
0.B) redhat
# yum install redis
0.C) from source
* see http://redis.io/download
1) Configuration
In srnd.ini the database sections should look like this:
[database]
type=redis
schema=single
host=localhost
port=6379
user=
password=
2) Run the daemon
* see the [next step](running.md)

77
install.sh Executable file
View File

@@ -0,0 +1,77 @@
#!/usr/bin/env bash
set -e
root=$(readlink -e $(dirname $0))
prefix="/opt/nntpchan"
help_text="usage: $0 [--prefix /opt/nntpchan] [-q|--quiet] [-r|--rebuild] [--disable-redis]"
# check for help flags first
for arg in $@ ; do
case $arg in
-h|--help)
echo $help_text
exit 0
;;
esac
done
_next=""
want_rebuild="0"
want_quiet="0"
build_args=""
# check for main flags
for arg in $@ ; do
case $arg in
-q|--quiet)
want_quiet="1"
;;
-r|--rebuild)
want_rebuild="1"
;;
--prefix)
_next="prefix"
;;
--prefix=*)
prefix=$(echo $arg | cut -d'=' -f2)
;;
--disable-redis)
build_args="$build_args --disable-redis"
;;
*)
if [ "X$_next" == "Xprefix" ] ; then
# set prefix
prefix="$arg"
_next=""
fi
;;
esac
done
_cmd() {
if [ "X$want_quiet" == "X1" ] ; then
$@ &> /dev/null
else
$@
fi
}
if [ "X$want_rebuild" == "X1" ] ; then
_cmd echo "rebuilding daemon";
_cmd $root/build.sh $build_args
fi
if [ ! -e $root/srndv2 ] ; then
_cmd echo "building daemon"
# TODO: use different GOPATH for root?
_cmd $root/build.sh $build_args
fi
_cmd mkdir -p $prefix
_cmd mkdir -p $prefix/webroot/thm
_cmd mkdir -p $prefix/webroot/img
_cmd cp -f $root/srndv2 $prefix/srndv2
_cmd cp -rf $root/{doc,contrib,certs} $prefix/
_cmd echo "installed to $prefix"