Compare commits

...

339 Commits
0.4.0 ... main

Author SHA1 Message Date
teddit 4536a7f460 Merge pull request 'Remove teddit.artemislena.eu' (#422) from artemislena/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/422
2023-09-17 15:21:03 +00:00
artemislena 4b2c1c4784
T.: Remove teddit.artemislena.eu 2023-09-16 02:48:58 +02:00
teddit ebb1b46af3 Update instances.json
add missing comma
2023-09-09 15:34:52 +00:00
teddit 8194c065c9 Merge pull request 'I added my instance' (#421) from lostskunk_Donetsk/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/421
2023-09-09 15:34:07 +00:00
lostskunk_Donetsk 227591acff Обновить instances.json 2023-09-09 15:31:30 +00:00
lostskunk_Donetsk 0f4c5030cd Обновить README.md 2023-09-08 08:09:51 +00:00
teddit 5997517825 Merge pull request 'Add t.sneed.network instance' (#420) from sneednet/teddit:sneednet-instance into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/420
2023-09-07 16:41:47 +00:00
sneednet 59abc28178 Update instances.json 2023-09-07 02:51:42 +00:00
sneednet 3f5acf0fce Add t.sneed.network instance 2023-09-07 02:46:31 +00:00
teddit 78973f2562 update packages 2023-08-23 19:34:59 +00:00
teddit a5b4bedfaa Merge pull request 'Add laserdisc.tokyo Instance' (#417) from x4/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/417
2023-08-23 19:33:31 +00:00
x4 2bb2efaca9 Add laserdisc.tokyo Instance 2023-08-17 15:20:39 +02:00
teddit 5ad837373a add mention about teddit no more being actively maintained due to Reddit API changes 2023-07-30 16:26:30 +02:00
teddit 242deb4e05 Merge pull request 'Catch errors when invalid data is received from Reddit in the user route' (#405) from ConcordOne/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/405
2023-07-30 14:07:31 +00:00
teddit 2f3e820fb4 Merge pull request 'Tedd.it and teddit.totaldarkness.net removed due to shutdown' (#412) from orca/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/412
2023-07-30 14:04:55 +00:00
cypherhades baa4fbd44e Tedd.it and teddit.totaldarkness.net removed due to shutdown 2023-07-15 12:01:28 -05:00
ConcordOne 8d800e490c
Catch errors when invalid data is received from Reddit in the user route 2023-06-06 20:19:01 +03:00
teddit b0ce6c52a6 remove inactive teddit.namazso.eu instance 2023-05-06 17:10:50 +02:00
teddit e6ae8bf0c3 Merge branch 'no-logs-main' into main 2023-05-05 21:47:47 +02:00
teddit 490556fc6a fix conflicts PR #399 2023-05-05 21:47:35 +02:00
No-Logs.com d05ecf2af6 Update 'instances.json' 2023-05-05 14:01:06 +00:00
No-Logs.com 04980be0d0 Update 'README.md' 2023-05-05 13:59:44 +00:00
teddit 0536dcd505 Merge pull request 'New Instance: teddit.xbdm.fun' (#401) from xbdm/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/401
2023-05-01 13:21:43 +00:00
Kyle David Rohland ec882c6869 New Instance: teddit.xbdm.fun 2023-04-30 23:11:07 +00:00
Kyle David Rohland 28bd75bdb5 New Instance: teddit.xbdm.fun 2023-04-30 23:10:22 +00:00
No-Logs.com 6a2641664f Update 'instances.json' 2023-04-29 05:03:19 +00:00
teddit 50b112bfbc add Project Segfault's onion instance URL 2023-04-27 20:52:27 +02:00
teddit a4558ce3ea add project segfault's instance (fixes #394) 2023-04-27 20:49:35 +02:00
teddit 6d8f27a09a Merge pull request 'Fix issues regarding preferences menu' (#395) from ppsn/teddit:frontend-repo-ref into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/395
2023-04-27 18:44:07 +00:00
ppsn 605e9064a1 corrected mistake 2023-04-27 17:48:27 +03:00
teddit d574b42297 Merge pull request 'change from debian stretch to latest (bullseye atm)' (#398) from zaggynl/teddit:zaggynl-fix-dockerfile into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/398
2023-04-27 14:00:34 +00:00
No-Logs.com 1fb2d45b06 Update 'README.md' 2023-04-27 13:43:32 +00:00
zaggynl e6ccc292b5 change from debian stretch to latest (bullseye atm)
as fails to build with stretch, as has been archived
2023-04-25 19:58:20 +00:00
ppsn 13e7a13c87 removed bibliogram 2023-04-10 12:16:24 +03:00
ppsn 76c57cc6aa added links to repos 2023-04-10 12:14:56 +03:00
teddit f73b9c2bea Merge pull request 'Add 'open in reddit.com' button' (#393) from ppsn/teddit:reddit-com-link into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/393
2023-04-05 20:12:10 +00:00
ppsn 809402937d corrected a mistake 2023-04-05 21:41:03 +03:00
ppsn 844b1d2056 Merge branch 'reddit-com-link' of https://codeberg.org/ppsn/teddit into reddit-com-link 2023-04-05 20:41:25 +03:00
ppsn f56ec2fb2f made some changes to link 2023-04-05 20:39:10 +03:00
ppsn 8e5f270783 Merge branch 'main' into reddit-com-link 2023-04-05 17:33:14 +00:00
teddit a23e606bee update legal text 2023-04-05 14:04:05 +02:00
teddit 5c147d0d82 Merge pull request 'Improve legal notice on README.md' (#390) from HexagonCDN/teddit:legal into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/390
2023-04-05 12:02:07 +00:00
teddit b9f108f675 remove inactive teddit.manasiwibi.com 2023-04-05 14:01:43 +02:00
teddit dbbb1ac6f7 Merge pull request 'remove inactive instance' (#388) from ManaSiWibi/teddit:remove into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/388
2023-04-05 12:00:18 +00:00
ppsn 2ac1466231 added 'open in reddit.com' button 2023-04-03 20:45:41 +03:00
HexagonCDN 298982dfff Improve legal notice on README.md 2023-03-08 14:13:09 +00:00
Wibi a02025a86f remove inactive instance 2023-03-05 03:21:22 +07:00
teddit 96a55fb2ce rename 'Invidious' => 'Invidious/Piped' (fixes #386) 2023-02-26 15:10:44 +01:00
teddit 21ddd3b89f update version 0.4.8 => 0.4.9 2023-02-26 14:28:28 +01:00
teddit 25e789fdad add legal jargon (fixes #369) 2023-02-26 14:26:01 +01:00
teddit 68aed7b9be add support for 7 character IDs for short urls (#290) 2023-02-26 13:47:10 +01:00
teddit 9cb6a80c15 update packages 2023-02-26 13:45:43 +01:00
teddit 8c52544404 Merge pull request 'Add logic to parse and remove any instances of https://twitter.com by replacing with user's preference' (#378) from anshumankmr/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/378
2023-02-26 12:22:46 +00:00
teddit 563be382d3 Merge pull request 'added else statements for prefer_frontpage and show_large_gallery_images' (#383) from The1029/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/383
2023-02-26 12:20:46 +00:00
The1029 596e8ef093 added else statements for prefer_frontpage and show_large_gallery_images 2023-01-22 11:51:32 +00:00
Anshuman Kumar 0969d694db Merge branch 'main' into main 2023-01-02 11:46:26 +00:00
Anshuman Kumar aa75cc239a Revert changes to package-lock.json and package.json 2023-01-02 08:34:50 +05:30
teddit 079ea36d24 update _options cookies, add option for opening gated subreddits, fixes #374 2023-01-01 21:55:03 +01:00
teddit d31c418816 update packages 2023-01-01 21:34:41 +01:00
teddit d11ddce9d2 Merge pull request 'Adding port expose to dockerfile' (#377) from bradmurray/teddit:bradmurray-dockerfile-expose into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/377
2023-01-01 20:28:14 +00:00
teddit 572068aca4 Merge pull request 'tweak: Add flag to pm2 setup command & improve docs slightly.' (#376) from NunoSempere/teddit:nunosempere-docs-tweak into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/376
2023-01-01 20:27:40 +00:00
teddit ea6ad7c8ec Merge pull request 'More API endpoints' (#373) from CosmosDev/teddit:feature/more_api_endpoints into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/373
2023-01-01 20:26:36 +00:00
Anshuman Kumar ed0c0c7d28 Handle all variation of user preference for regexes 2022-12-30 17:43:43 +05:30
Anshuman Kumar 9a328612c4
Merge pull request #1 from anshumankmr/dependabot/npm_and_yarn/node-fetch-3.3.0
Bump node-fetch from 2.6.7 to 3.3.0
2022-12-30 17:25:52 +05:30
dependabot[bot] e28ddf2935
Bump node-fetch from 2.6.7 to 3.3.0
Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 2.6.7 to 3.3.0.
- [Release notes](https://github.com/node-fetch/node-fetch/releases)
- [Commits](https://github.com/node-fetch/node-fetch/compare/v2.6.7...v3.3.0)

---
updated-dependencies:
- dependency-name: node-fetch
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-30 11:55:45 +00:00
Anshuman Kumar b3b1cbbc95
Merge pull request #2 from anshumankmr/dependabot/npm_and_yarn/redis-4.5.1
Bump redis from 3.1.2 to 4.5.1
2022-12-30 17:25:29 +05:30
Anshuman Kumar b6acf7542a
Merge pull request #3 from anshumankmr/dependabot/npm_and_yarn/postman-request-2.88.1-postman.31
Bump postman-request from 2.88.1-postman.30 to 2.88.1-postman.31
2022-12-30 17:25:16 +05:30
Anshuman Kumar 2873e2aea5
Merge pull request #4 from anshumankmr/dependabot/npm_and_yarn/https-proxy-agent-5.0.1
Bump https-proxy-agent from 5.0.0 to 5.0.1
2022-12-30 17:25:04 +05:30
Anshuman Kumar ab95c341c6 Merge branch 'main' of https://github.com/anshumankmr/teddit 2022-12-30 09:48:34 +05:30
Anshuman Kumar f7f2d0beec Update regex to find a match and replace if present 2022-12-30 09:42:08 +05:30
dependabot[bot] f7cecfd465
Bump https-proxy-agent from 5.0.0 to 5.0.1
Bumps [https-proxy-agent](https://github.com/TooTallNate/node-https-proxy-agent) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/TooTallNate/node-https-proxy-agent/releases)
- [Commits](https://github.com/TooTallNate/node-https-proxy-agent/compare/5.0.0...5.0.1)

---
updated-dependencies:
- dependency-name: https-proxy-agent
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-30 03:41:34 +00:00
Anshuman Kumar df20a9c7fe
Merge pull request #5 from anshumankmr/dependabot/npm_and_yarn/helmet-6.0.1
Bump helmet from 4.6.0 to 6.0.1
2022-12-30 09:11:11 +05:30
dependabot[bot] 0ea8b831c3
Bump helmet from 4.6.0 to 6.0.1
Bumps [helmet](https://github.com/helmetjs/helmet) from 4.6.0 to 6.0.1.
- [Release notes](https://github.com/helmetjs/helmet/releases)
- [Changelog](https://github.com/helmetjs/helmet/blob/main/CHANGELOG.md)
- [Commits](https://github.com/helmetjs/helmet/compare/v4.6.0...v6.0.1)

---
updated-dependencies:
- dependency-name: helmet
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-29 16:59:08 +00:00
dependabot[bot] 625c86a2b2
Bump postman-request from 2.88.1-postman.30 to 2.88.1-postman.31
Bumps [postman-request](https://github.com/postmanlabs/postman-request) from 2.88.1-postman.30 to 2.88.1-postman.31.
- [Release notes](https://github.com/postmanlabs/postman-request/releases)
- [Changelog](https://github.com/postmanlabs/postman-request/blob/master/CHANGELOG.md)
- [Commits](https://github.com/postmanlabs/postman-request/compare/v2.88.1-postman.30...v2.88.1-postman.31)

---
updated-dependencies:
- dependency-name: postman-request
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-29 16:58:48 +00:00
dependabot[bot] a9c8f6b6ce
Bump redis from 3.1.2 to 4.5.1
Bumps [redis](https://github.com/redis/node-redis) from 3.1.2 to 4.5.1.
- [Release notes](https://github.com/redis/node-redis/releases)
- [Changelog](https://github.com/redis/node-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/node-redis/compare/v3.1.2...redis@4.5.1)

---
updated-dependencies:
- dependency-name: redis
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-29 16:58:42 +00:00
Anshuman Kumar c71bf6ee48 Add logic to parse and remove any instances of https://twitter.com by replacing with user's preference
The issue was happening since the original regex is checking for href tags only, so I tried a simpler approach of searching for https://twitter.com instead whenever the regex does not match the input string

Add Nodemon package to dev dependencies for local debugging
2022-12-29 20:21:09 +05:30
bradmurray d96372b08a Adding port expose to dockerfile
If you want to use a dockerized nginx the dockerfile has to have an expose command or no ports get exposed.
2022-12-27 02:07:31 +00:00
NunoSempere e59e57ca83 tweak: Add name flag to pm2, make docs a bit more explanatory. 2022-12-25 00:37:12 +00:00
NunoSempere 494c7f5417 Merge pull request 'update: update fork before making new pull request.' (#1) from teddit/teddit:main into main
Reviewed-on: https://codeberg.org/NunoSempere/teddit/pulls/1
2022-12-25 00:32:20 +00:00
CosmosDev 2c5c9d7cde Add support for subreddits explore API 2022-12-15 22:03:49 +01:00
CosmosDev 54c658ff9b Clean code 2022-12-15 21:38:21 +01:00
CosmosDev 3a51e9d165 Refactor handleSubreddit.js 2022-12-15 20:25:04 +01:00
CosmosDev baddb1aec2 Add suport for subreddit search API 2022-12-15 20:21:12 +01:00
CosmosDev eec2a24eb3 Add support for subreddit about API 2022-12-15 19:10:32 +01:00
CosmosDev 0852396b6d Add support for post API 2022-12-14 22:08:12 +01:00
teddit 22b63a3415 Merge pull request 'add imgur replacement feature (#341)' (#365) from eUgEntOptIc44/teddit:eugentoptic44-add-rimgo into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/365
2022-12-11 15:32:09 +00:00
Jean-Luc Tibaux 50c369833b improved imgur regex pattern as requested in #365 2022-11-26 20:22:01 +00:00
Jean-Luc Tibaux 45dc798eef implement imgur replacement (#341) 2022-11-12 13:47:08 +00:00
teddit e2237f1b93 Merge pull request 'upgrade github actions' (#363) from eUgEntOptIc44/teddit:eUgEntOptIc44-update-gh-actions into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/363
2022-11-09 16:15:55 +01:00
teddit ee7068a88c Merge pull request 'Fix theme select dropdown on invalid cookie value, add height to topbar icon' (#364) from kazuki/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/364
2022-11-09 16:14:40 +01:00
kazuki dc58122aa1
Spacing 2022-11-08 08:10:31 +01:00
kazuki ba207e18c7
Add height to Teddis topbar icon 2022-11-08 08:08:49 +01:00
kazuki 4241490793
Fix theme select dropdown on invalid cookie value 2022-11-08 07:59:04 +01:00
Jean-Luc Tibaux d70e6a4265 upgrade github actions 2022-10-30 11:38:02 +00:00
teddit bb06a47b1a Merge pull request 'Consistent padding for input and select elements, add 2 spaces' (#362) from ltGuillaume/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/362
2022-10-29 20:54:10 +02:00
ltGuillaume eb2131c2ff - Consistent padding for input and select elements
- Add spaces to Bibliogram and Quetre labels for consistency
2022-10-29 20:40:18 +02:00
teddit 4c3c743536 Merge pull request 'Corrected privacytools instance url' (#360) from Hygna/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/360
2022-10-28 17:16:24 +02:00
teddit 66b11dc647 Merge pull request 'Add Hostux Instance' (#359) from valere/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/359
2022-10-28 17:15:55 +02:00
Hygna 83a8c48d9c
Corrected privacytools teddit instance url 2022-10-24 16:08:02 +01:00
valere ba3230be1a Mise à jour de 'README.md' 2022-10-23 20:31:41 +02:00
valere 0003bdcbb8 Mise à jour de 'instances.json' 2022-10-23 20:31:16 +02:00
teddit 6fa109e6ff Merge pull request 'tweak: Add documentation to daemonize process' (#354) from NunoSempere/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/354
2022-10-18 16:37:26 +02:00
teddit a4b130af81 Merge pull request 'Add Quetre option' (#356) from alan2b7/teddit:add-quetre-option into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/356
2022-10-18 16:31:29 +02:00
teddit 1ece7467dc Merge pull request 'Add preview.redd.it to replaceable_media_domains' (#355) from austinhuang/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/355
2022-10-18 16:30:43 +02:00
alan bc3af7b609 Add Quetre option 2022-10-15 06:23:48 +02:00
Austin Huang 0cf3d6d6c9
Add preview.redd.it to replaceable_media_domains
Since you already replace preview.redd.it links like so in post contents
2022-10-15 00:20:41 -04:00
NunoSempere 718d04c412 fix: typo 2022-10-14 21:28:48 +01:00
NunoSempere dcdfc28e00 tweak: Add documentation to daemonize process
when running teddit not on a docker container.

I've generally found docker gnarly, and try not to use it. In this
case, I find pm2 to be a more convenient alternative.

It might also be interesting to have better documentation for running
this from a reverse proxy, maybe step by step, so that more people
can host these instances.
2022-10-14 21:23:42 +01:00
teddit 27874a6766 Merge pull request 'update nord infobar bg' (#352) from apmechev/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/352
2022-10-05 18:18:01 +02:00
teddit e5640ef5a3 remove nautolan.racing for using tracking JS (#351) 2022-10-05 18:15:14 +02:00
teddit facab379ca Merge pull request 'Fixed some instances' (#350) from Hygna/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/350
2022-10-05 18:12:36 +02:00
apmechev e5d374f5f8
update nord infobar bg 2022-10-04 09:43:35 +02:00
Hygna ba8ed2acb0
Fixed some instances 2022-10-03 18:42:05 +01:00
teddit ccfdff679e Merge pull request 'Add 24fympskbrdgbf4afuvhqwwl2tv3y2vwxg5t2ktozd4j5b3fob5ntzyd.onion and teddit.manasiwibi.com' (#349) from ManaSiWibi/teddit:add-instances into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/349
2022-09-16 18:36:05 +02:00
Wibi b66e4159d2 update readme 2022-09-13 01:24:15 +08:00
Wibi 738a7ba646 Add Instances 2022-09-13 01:20:17 +08:00
teddit 48b89a7a8c add missing comma (fixes #348) 2022-09-09 04:41:00 +02:00
teddit 52bc1f4945 rewrite urls for embedded Reddit media in post comments (#345) 2022-09-06 20:08:19 +02:00
teddit 55b17e5141 add onion link for privacytools.io (#343) 2022-09-06 18:29:31 +02:00
teddit 90f0872d77 Merge pull request 'L: Add teddit.artemislena.eu' (#344) from artemislena/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/344
2022-09-06 18:25:07 +02:00
artemislena bccae3cce5
L: s/reddit/teddit/ 2022-09-06 12:26:18 +02:00
artemislena e5cc5c8d3b
L: Add reddit.artemislena.eu 2022-09-05 19:19:06 +02:00
teddit 67b746c4df Merge pull request 'Add new Instance teddit.rawbit.ninja' (#340) from technonerd33/teddit:rawbit-instance into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/340
2022-08-20 11:21:41 +02:00
technonerd e120ecfca2
Add instance: teddit.rawbit.ninja to README.md 2022-08-19 16:14:16 -04:00
technonerd c515f351be
Add instance: teddit.rawbit.ninja to instances.json 2022-08-19 16:12:25 -04:00
teddit d85f0680ad Merge pull request 'Add ~vern instances' (#339) from cobra/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/339
2022-08-14 11:49:00 +02:00
Moxie "The Cobra" Widulski dbe25af3ac Add ~vern instances to README.md
Signed-off-by: Moxie "The Cobra" Widulski <thecobra@riseup.net>
2022-08-14 08:59:01 +02:00
Moxie "The Cobra" Widulski e697729466 Add ~vern instances to instances.json
Signed-off-by: Moxie "The Cobra" Widulski <thecobra@riseup.net>
2022-08-14 08:49:11 +02:00
teddit cee89787ea Merge pull request 'Added PrivacyTools.io instance' (#338) from PrivacyTools/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/338
2022-08-13 13:27:53 +02:00
PrivacyTools 163bfe8e4d Added PrivacyTools.io instance
https://wiki.privacytools.io
2022-08-13 13:27:07 +02:00
PrivacyTools 09ac20dd6b Added PrivacyTools.io instance
https://teddit.privacytools.io
2022-08-13 08:15:08 +02:00
teddit 07984ab36b Merge pull request 'Add tedd.it to instances list' (#335) from ConcordOne/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/335
2022-07-21 19:47:42 +02:00
ConcordOne baba8b7fab Update 'README.md' 2022-07-21 19:03:40 +02:00
ConcordOne 0da38c783a Add `tedd.it` to `instances.json` 2022-07-21 18:59:23 +02:00
teddit cbd0babf43 add garudalinux's instance to the instances.json (pr #334) 2022-07-20 17:22:46 +02:00
teddit 25fa819833 Merge pull request 'Add Garudalinux' instance' (#334) from dr460nf1r3/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/334
2022-07-20 17:21:45 +02:00
dr460nf1r3 372f21328f
Add Garudalinux' instance 2022-07-13 15:50:17 +02:00
teddit 5f1d50bc8c Merge pull request 'adds nord dark theme' (#333) from apmechev/teddit:add_nord_theme into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/333
2022-07-08 04:32:48 +02:00
apmechev 18b26aa758 adds nord dark theme 2022-07-07 23:00:33 +02:00
teddit 7ef04def16 Merge pull request 'Integrate Reveddit with deleted comments' (#329) from piislide/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/329
2022-07-07 14:43:23 +02:00
Abdelkarim Djelalda f7082acc41 change deleted user's comment to #cc6a6a 2022-07-07 14:35:31 +01:00
Abdelkarim Djelalda f33d78f6a4 Integrate Reveddit with deleted comments 2022-07-06 01:44:50 +01:00
teddit 74625f39b7 add instance teddit.tokhmi.xyz (#328) 2022-07-02 23:42:56 +02:00
teddit 799bc837c7 update version 0.4.7 => 0.4.8 2022-06-27 21:52:18 +02:00
teddit 07050c9cf6 properly fix 'continue this thread' links - no need to req.url checking (ref #82) 2022-06-27 21:48:40 +02:00
teddit 2b22ffa105 update version 0.4.6 => 0.4.7 2022-06-27 03:39:18 +02:00
teddit 02fd92ce2c fix 'continue this thread' links (fixes #82) 2022-06-27 03:23:29 +02:00
teddit 8af3ec3591 Merge pull request 'Remove https://teddit.alefvanoon.xyz from instance list' (#326) from triallax/teddit:remove-alefvanoon-instance into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/326
2022-06-27 02:08:30 +02:00
triallax e29b41a728 Remove https://teddit.alefvanoon.xyz from instance list
The instance (and https://alefvanoon.xyz in general) doesn't work
anymore. https://github.com/alefvanoon/services.alefvanoon.xyz and
https://github.com/alefvanoon/alefvanoon.xyz are both archived, so it's
likely alefvanoon took the website down.
2022-06-24 21:14:00 +03:00
teddit e3b2c663d9 add instance i.opnxng.com (#324) 2022-06-22 14:57:57 +02:00
teddit 842a7ca802 fix trom.tf link (fixes #322) 2022-06-20 16:02:18 +02:00
teddit 0863c841e4 add teddit.encrypted-data.xyz to the instance list (#320) 2022-06-12 17:29:26 +02:00
teddit 84d0009984 add an example about setting the suggested_subreddits array with env vars 2022-06-11 22:59:12 +02:00
teddit a0c74aa309 fix sortby key, should be confidental and not best 2022-06-11 21:55:32 +02:00
teddit 4663b3a78d Merge pull request 'Add setting for preferred default comment sorting' (#319) from redmt/teddit:default_comment_sorting into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/319
2022-06-11 21:44:47 +02:00
redmt 4ff7152be1 Add setting for preferred default comment sorting 2022-06-11 21:40:12 +02:00
teddit b1f65f31d8 Merge pull request 'Fix error in the search route' (#318) from StevenNMeza/teddit:fix_search_error into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/318
2022-06-11 21:35:49 +02:00
StevenNMeza 231348e75e Fix error in the search route 2022-06-11 21:34:00 +02:00
teddit 1fbef42a7c Merge pull request 'Allow optional large image display with captions for galleries' (#317) from redmt/teddit:large_gallery_images_with_caption into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/317
2022-06-11 21:11:12 +02:00
redmt 9fd891b474 Add setting to show large gallery images with captions 2022-06-11 21:04:55 +02:00
redmt 0bf27cfd6c Update post.pug and styles.css for large gallery images 2022-06-11 20:59:35 +02:00
redmt fc56f0a833 Update processJsonPost.js to include gallery image captions 2022-06-11 20:55:14 +02:00
teddit 1759724268 add the suggested_subreddits config to the list of env vars 2022-06-11 20:03:13 +02:00
teddit 601e393dda Merge pull request 'Add .vscode to .gitignore' (#316) from redmt/teddit:add_dotvscode_to_gitignore into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/316
2022-06-11 20:01:16 +02:00
redmt d8edffba1a Add .vscode to .gitignore 2022-06-11 19:58:32 +02:00
teddit 270e9e5ceb update version 0.4.5 => 0.4.6 2022-06-11 19:56:48 +02:00
teddit 28c3c60078 add missing config require 2022-06-11 19:53:25 +02:00
teddit 7f981f69ae Merge pull request 'Add suggested_subreddits option to config.js' (#315) from redmt/teddit:default_subreddits_as_config_option into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/315
2022-06-11 19:48:17 +02:00
redmt 1f6f5d621d Update user.js to pass instance_config to template 2022-06-11 19:40:37 +02:00
redmt 53a5766de7 Update static.js to pass instance_config to template 2022-06-11 19:40:36 +02:00
redmt a0fcdf101e Update search.js to pass instance_config to template 2022-06-11 19:40:34 +02:00
redmt 55bd044b06 Update save.js to pass instance_config to template 2022-06-11 19:40:32 +02:00
redmt b2dba3f723 Update preferences.js to pass instance_config to template 2022-06-11 19:40:31 +02:00
redmt 8f57cb143b Update home.js to pass instance_config to template 2022-06-11 19:40:29 +02:00
redmt 69a9b0e080 Update inc/initRedditApi.js to pass instance_config to template 2022-06-11 19:40:27 +02:00
redmt afbad4ae3b Update subreddit.js to pass instance_config to template 2022-06-11 19:40:26 +02:00
redmt 20db540b5c Update topbar.pug to use suggested_subreddits 2022-06-11 19:40:23 +02:00
redmt cfdfd4a2b1 Rename internal_config to instance_config for homepage.pug 2022-06-11 19:40:16 +02:00
redmt 555ff9068b Fix search form width for homepage.pug 2022-06-11 19:40:13 +02:00
redmt 2ff979bdda Update homepage.pug to show suggested subreddits 2022-06-11 19:40:00 +02:00
redmt 108f87a5e4 Add suggested_subreddits option to config.js.template 2022-06-11 19:39:38 +02:00
teddit b0d5377e21 update packages and update package-lock.json (fixes #313) 2022-06-11 18:33:30 +02:00
teddit 0432c7484d hide top links from the top bar in the cleaned homepage view 2022-06-11 14:00:01 +02:00
teddit a63b5beb7e add top bar to the cleaned homepage, and also fix the homepage pug markup (by adding commas after attrs) 2022-06-11 13:56:37 +02:00
teddit 60d9b33825 update version 0.4.4 => 0.4.5 2022-06-11 13:48:41 +02:00
teddit c9b2dbf750 fix identation 2022-06-11 13:45:15 +02:00
teddit b797cef167 fix clean_homepage feature so that the user can enable or disable it despite of the instance config 2022-06-11 13:42:19 +02:00
teddit 096c7218d4 Merge pull request 'Add cleaned homepage' (#312) from redmt/teddit:add_clean_homepage into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/312
2022-06-11 11:51:47 +02:00
redmt fb6673265e Add cleaned homepage 2022-06-10 22:24:58 +02:00
redmt 546fdb5d77 Convert 'on' value in prefer_frontpage setting to 'true' 2022-06-10 22:22:37 +02:00
redmt d368acac29 Hide homepage setting when it's disabled site-wide 2022-06-10 22:21:54 +02:00
redmt d6b2e0700a Fix wrong rename for frontpage.pug 2022-06-10 21:45:18 +02:00
redmt 6c52664155 Add setting to preferences for preferred homepage mode 2022-06-10 21:24:13 +02:00
redmt 27ade36805 Rename index.pug to frontpage.pug 2022-06-10 20:05:36 +02:00
redmt 354180119f Add clean_homepage option to config.js.template 2022-06-10 20:03:25 +02:00
teddit 3855a618ee update version 0.4.3 => 0.4.4 2022-06-10 19:32:34 +02:00
teddit 9e4ea80c7e check that subreddit_about exists (sometimes it might be null and referencing to the public_description_html will throw an error 2022-06-10 19:30:24 +02:00
teddit 1c9b125b70 Merge pull request 'Add OpenGraph meta tags for rich embeds' (#311) from redmt/teddit:add_opengraph_meta_tags_for_embeds into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/311
2022-06-10 19:14:31 +02:00
redmt 7f8cc27ffb Add og: meta tags in user.pug 2022-06-10 16:10:28 +02:00
redmt 7a508c55ee Add og: meta tags in subreddits_explore.pug 2022-06-10 16:10:14 +02:00
redmt 05d7db481c Add og: meta tags in subreddit_wiki.pug 2022-06-10 16:09:54 +02:00
redmt ae429bcbf9 Add og: meta tags in search.pug 2022-06-10 16:09:40 +02:00
redmt a73bd2e3ec Add og: meta tags in saved.pug 2022-06-10 16:09:28 +02:00
redmt 9c8e72eccd Add og: meta tags in privacypolicy.pug 2022-06-10 16:09:14 +02:00
redmt 0bf3ead82d Add og: meta tags in preferences.pug 2022-06-10 16:08:58 +02:00
redmt cc6c2a1cd6 Add fallback logo for text-only posts in meta_post.pug 2022-06-10 16:01:39 +02:00
redmt 6328e41bcf Update processJsonPost.js to remove newlines from selftext_preview 2022-06-10 15:56:54 +02:00
redmt 319fa35d5f Update processJsonPost.js to store a preview selftext 2022-06-10 15:42:37 +02:00
redmt 064c97579e Fix invalid indentation in meta_post.pug 2022-06-10 15:27:41 +02:00
redmt 811348c1af Add og: meta tags for subreddit.pug 2022-06-10 15:20:56 +02:00
redmt 2f51368ac6 Move common og: meta definitions to include files 2022-06-10 15:20:06 +02:00
redmt 932d7f07ee Move post meta definition to separate file 2022-06-10 15:18:36 +02:00
redmt 5ef61af67f Add og:type property to frontpage 2022-06-10 12:03:23 +02:00
redmt 04395734cf Remove og:image:url property for gallery posts 2022-06-10 11:58:32 +02:00
redmt 2a92f0a57d Add og: meta tags to post.pug 2022-06-10 11:30:58 +02:00
redmt 12790f7e8e Add og: meta tags to about.pug 2022-06-10 11:30:44 +02:00
redmt 22328e318c Add og: meta tags to index.pug 2022-06-10 11:30:13 +02:00
redmt 7ceb0e8b5d Add og:site_name meta tag to head.pug 2022-06-10 11:12:56 +02:00
redmt 9553b94cc0 Add manually upscaled application logo to static content 2022-06-10 11:11:57 +02:00
teddit 8ccc694d2f fix media proxying (clicking preview.redd.it image links was broken after 3612345fab 2022-05-01 19:14:39 +02:00
teddit e640e794db update version 0.4.2 => 0.4.3 2022-04-11 21:32:52 +02:00
teddit 3612345fab proxy i.redd.it and v.redd.it media links in comments (#307) 2022-04-11 21:30:41 +02:00
teddit 1b6e1e5494 fix rdt.trom.tf link 2022-03-30 19:56:40 +02:00
teddit 77917c8ea5 Merge pull request 'When self-hosting teddit with https disabled, generated links often still contain https' (#302) from analogue/teddit:origin/fix_static_links_when_https_disabled into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/302
2022-03-30 19:54:50 +02:00
analogue 9b6dec1108 Merge branch 'main' into origin/fix_static_links_when_https_disabled 2022-03-29 22:24:56 +02:00
analogue 4bb02eaf03 Use proper https_enabled flag 2022-03-29 20:22:30 +00:00
teddit 09ca31701a add instance rdt.trom.tf (#304) 2022-03-29 17:36:48 +02:00
teddit 925fe0565e Merge pull request 'Remove teddit.kavin.rocks from instances.json' (#305) from triallax/teddit:remove-teddit-kavin-rocks-from-instances-file into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/305
2022-03-29 17:28:19 +02:00
teddit 86dab3b816 Merge pull request 'Add teddit.froth.zone to the instance list' (#303) from sammefishe/teddit:teddit.froth.zone into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/303
2022-03-29 17:27:38 +02:00
triallax 87bd0138c1 Remove teddit.kavin.rocks from instances.json 2022-03-29 16:51:52 +02:00
Sam Therapy 4626a0f9bf
Add teddit.froth.zone to the instance list
Signed-off-by: Sam Therapy <sam@samtherapy.net>
2022-03-28 22:13:29 +02:00
analogue ee7508b24a When teddifying URLs to se for links, images, or video, use correct protocol scheme 2022-03-13 22:13:21 +00:00
teddit a53a9fc3df Merge pull request 'Show imgur videos' (#299) from StevenNMeza/teddit:show_imgur_videos into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/299
2022-03-04 20:55:05 +01:00
teddit 5c8cbc8595 Merge pull request 'Remove teddit.kavin.rocks from instance list (doesn't work anymore)' (#301) from triallax/teddit:remove-kavin-rocks-instance into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/301
2022-03-04 20:51:39 +01:00
triallax 2b03532289 Remove teddit.kavin.rocks from instance list
I was told by the instance owner that it was shut down due to "an issue
with disk space usage."
2022-03-04 20:49:03 +01:00
StevenNMeza a89ab7d1b5 Show imgur videos 2022-02-24 19:18:09 +01:00
teddit 2d69623bf1 Merge pull request 'Add wget to the Docker so that the healthcheck works' (#295) from TheFrenchGhosty/teddit:wget-docker into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/295
2022-02-08 18:56:58 +01:00
TheFrenchGhosty 1baa654a54 Add wget to the Docker so that the healthcheck works 2022-02-08 18:52:25 +01:00
teddit fa8f0ba02b fix instance onion link (fixes #294) 2022-02-03 18:19:32 +01:00
teddit 8bcbe8d4ee fix instance onion link (fixes #294) 2022-02-03 18:06:26 +01:00
teddit 6c84bdd1e3 set show_upvotes checked by default 2022-01-31 19:21:13 +01:00
teddit d71c88871c Merge pull request 'Add option to hide posts upvotes and comments points' (#293) from sartateme/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/293
2022-01-31 19:18:58 +01:00
sartateme 274a8be24e Add option to hide posts upvotes and comments points 2022-01-31 15:31:00 +01:00
teddit 3ef2f004ea Merge pull request 'Improve The Way Long Source URLs Are Displayed' (#292) from amrw/teddit:improve-long-source-url-display into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/292
2022-01-30 21:04:56 +01:00
amrw 063523ed4d
Improve how long source URLs are displayed 2022-01-30 20:42:29 +01:00
teddit 7c7709e74f improve subreddit search, include sort and t params (#204) 2022-01-30 01:33:58 +01:00
teddit eac2627114 update version 0.4.1 => 0.4.2 2022-01-30 00:01:28 +01:00
teddit 265c33fcd2 use 24 hours interval if the interval value (config.cache_control_interval) is set to over 1000. fixes cache control for older config.cache_control_interval values 2022-01-29 23:53:51 +01:00
teddit 01c41c7adf link instances.json to ./static/instances.json (to make it accessible via instances) 2022-01-29 15:35:04 +01:00
teddit 397a02c821 add instances file, instances.json 2022-01-29 15:25:08 +01:00
teddit 3ad64d5c87 use apt-get instead of apt 2022-01-29 14:53:45 +01:00
teddit d315051a7b fix mime type in POST import_prefs 2022-01-29 14:49:47 +01:00
teddit d947182058 Merge pull request 'Use slim node image instead of alpine' (#288) from austinhuang.me/teddit:austinhuang.me-patch-1 into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/288
2022-01-29 14:40:41 +01:00
teddit e87b811883 Merge pull request 'Support redd.it short url redirections' (#290) from austinhuang.me/teddit:austinhuang.me-patch-3 into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/290
2022-01-29 14:40:08 +01:00
teddit 446f388be2 Merge pull request 'Improve domain replacement regex' (#289) from austinhuang.me/teddit:austinhuang.me-patch-2 into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/289
2022-01-29 14:38:44 +01:00
teddit 791a80b337 fix invalid mime type (fixes #291) 2022-01-29 14:35:03 +01:00
teddit 301774ce8d update node-fetch@2.6.7 - fixes CVE-2022-0235 (teddit is not affected) 2022-01-29 14:27:14 +01:00
Austin Huang 08437d8663
Support redd.it short url redirections
Close #273
2022-01-26 13:56:40 -05:00
Austin Huang b5878ea031
Improve domain replacement regex
1. Let regex only match href, close #267
2. Enforce HTTPS if the instance is on HTTPS
3. Prevent redirecting domains such as blog.twitter.com and help.instagram.com
2022-01-26 12:41:18 -05:00
Austin Huang c97fb5a517
Use slim node image instead of alpine
Signed-off-by: Austin Huang <im@austinhuang.me>
2022-01-26 11:49:47 -05:00
teddit 1efe34c0ad add description for config.api_force_https 2022-01-18 19:54:26 +01:00
teddit 7a9603f850 add teddit.bus-hit.me instance (#285) 2022-01-18 19:52:08 +01:00
teddit 15256b1cd8 add a config to force https for api permalinks (#285) 2022-01-18 19:47:16 +01:00
teddit bc274e9660 Merge pull request 'change markdown code blocks in README.md to improve syntax highlighting' (#283) from eUgEntOptIc44/teddit:eugentoptic44-improve-syntax-highlighting into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/283
2022-01-13 17:30:14 +01:00
teddit ab44566561 Merge pull request 'add dependabot config for npm dependencies' (#282) from eUgEntOptIc44/teddit:eugentoptic44-npm-dependabot-scanning into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/282
2022-01-13 17:29:53 +01:00
teddit a411df0503 Merge pull request 'Fix forced configuration options' (#276) from nfusionz/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/276
2022-01-13 17:22:12 +01:00
Jean-Luc Tibaux 849475925d changed markdown code blocks to improve syntax highlighting
changed from e.g. console -> docker to improve general readability
2022-01-12 09:25:22 +01:00
Jean-Luc Tibaux dd45b2633a add dependabot config for npm dependencies
for dependabot config docs -> please also see: https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates#package-ecosystem
2022-01-10 18:03:31 +01:00
nfusionz fcc0fce890 Some config settings set to default to true
- If the environmental variable isn't set, default to true.
2021-12-29 22:09:43 -05:00
nfusionz c1faeeb05c Fix forced configuration options 2021-12-29 21:44:57 -05:00
teddit 734ac6a817 Merge pull request 'Fix: API light mode subreddit' (#279) from gawii0/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/279
2021-12-29 00:08:32 +01:00
gawii0 2e030e2c4b Fix: API light mode subreddit 2021-12-28 22:58:34 +01:00
teddit 010c934d60 update dependencies 2021-12-27 18:51:13 +01:00
teddit ab076d020f Merge pull request 'API light mode subreddit' (#277) from gawii0/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/277
2021-12-27 18:25:08 +01:00
gawii0 0d2de61444 Update handleTedditApiSubreddit 'routes/user.js' 2021-12-27 18:19:05 +01:00
gawii0 e870b9ea50 Update handleTedditApiSubreddit 'routes/subreddit.js' 2021-12-27 18:18:21 +01:00
gawii0 e0bf08132d Update handleTedditApiSubreddit 'routes/home.js' 2021-12-27 18:17:15 +01:00
gawii0 aa3a854b6e API light mode subreddit 2021-12-24 15:37:46 +01:00
teddit 8315ad3f16 make the preference show_upvoted_percentage configurable via config.js #274 2021-12-21 22:19:38 +01:00
teddit 4bf092e481 Merge pull request 'Add an "Operated by" note to the PussTheCat.org instance' (#272) from TheFrenchGhosty/teddit:operated-by-pussthecat-org into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/272
2021-12-18 21:46:07 +01:00
TheFrenchGhosty cadf67f488 Add an "Operated by" note to the PussTheCat.org instance 2021-12-18 21:28:56 +01:00
teddit 992429818f Merge pull request 'add teddit.adminforge.de' (#271) from dominion/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/271
2021-12-18 14:30:18 +01:00
dominion 2ea766e01a „README.md“ ändern 2021-12-18 14:28:26 +01:00
dominion fd3d96a8d3 add teddit.adminforge.de 2021-12-18 14:27:56 +01:00
teddit be28d7fdfa Merge pull request 'Add instance teddit.totaldarkness.net' (#270) from TotalDarkness/teddit:main into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/270
2021-12-18 14:18:33 +01:00
TotalDarkness 168d896e69 Add instance teddit.totaldarkness.net 2021-12-18 03:46:53 -05:00
teddit 126eb5d0cc Merge pull request 'docs(instances): iba...onion is associated with xug...i2p' (#269) from curben/teddit:onion-i2p into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/269
2021-12-12 11:51:50 +01:00
Ming Di Leom 0cf6a2228f
docs(instances): iba...onion is associated with xug...i2p
- https://mdleom.com/about/#Services
2021-12-11 05:41:17 +00:00
teddit b476f9f2b6 update dependencies 2021-11-30 14:40:29 +01:00
teddit 0eb5ce1b6e Merge pull request 'Proxyable embeds for imgur' (#249) from 3np/teddit:imgur-proxy into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/249
2021-11-27 15:07:48 +01:00
teddit c9637524ec set cache_control_interval in hours instead of milliseconds 2021-11-27 14:57:38 +01:00
teddit 9783091216 mention that config.cache_control_interval is in milliseconds 2021-11-26 23:04:46 +01:00
teddit 08e9f90233 update the descriptions for cache_control keys 2021-11-26 21:46:24 +01:00
teddit 30d0000dca fix the default config.cache_control_interval to 24 hours instead of 30 minutes 2021-11-26 21:34:51 +01:00
teddit d895e400ef rewrite the cache control script 2021-11-26 21:33:12 +01:00
3nprob 8bf7d4f792 Support implicit video media 2021-11-23 05:28:27 +09:00
3nprob 56a2f6b266 Rewrite i.imgur.com gifv to mp4
* Configurable env var VALID_MEDIA_DOMAINS
2021-11-23 05:28:22 +09:00
teddit 660e5681be Merge pull request 'Add teddit.sethforprivacy.com instance.' (#264) from sethforprivacy/teddit:sethforprivacy-patch-1 into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/264
2021-11-22 20:48:11 +01:00
sethforprivacy 631b63c6eb Add teddit.sethforprivacy.com instance. 2021-11-22 20:44:29 +01:00
teddit 59bacc0fde Merge pull request 'Adding reddit.lol to public instances' (#263) from tacerus/teddit:public-instances-patch-01 into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/263
2021-11-22 18:46:03 +01:00
tacerus a37d20f889 Adding reddit.lol to public instances
Signed-off-by: tacerus <tacerus@noreply.codeberg.org>
2021-11-22 01:58:30 +01:00
teddit f8e6d326ba disallow Applebot, crawls way too much 2021-11-18 20:29:24 +01:00
teddit 998fa01a39 Merge pull request 'Add a production-ready compose and enhance the development one' (#256) from TheFrenchGhosty/teddit:better-compose into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/256
2021-11-04 16:23:10 +01:00
TheFrenchGhosty adf8258670 Add a note for a production-ready teddit not behind a reverse proxy 2021-11-04 16:17:54 +01:00
TheFrenchGhosty db63adcb7d Add a comment to the development compose 2021-11-04 16:13:57 +01:00
TheFrenchGhosty 43f846c1e5 Remove the useless environment variables added by mistake to the development compose 2021-11-04 16:12:10 +01:00
TheFrenchGhosty eb8e1f394b Add a production-ready compose and enhance the development one 2021-11-04 16:05:31 +01:00
teddit 692d6164b3 update dependencies 2021-10-30 19:32:35 +02:00
teddit c34c32cc35 check that reddit_video_preview.fallback_url exists (fixes #255) 2021-10-30 19:26:54 +02:00
teddit a9f13741bb check that post media_metadata exists (fixes #250) 2021-10-08 06:46:37 +02:00
teddit 5a01338428 update dependencies - fixes security vulns on loadash 2021-10-07 17:54:56 +02:00
teddit c4d167c787 validate json before processing it (fixes #247) 2021-10-06 17:38:08 +02:00
teddit d475407061 use fullchain.pem instead of chain.pem 2021-10-04 20:28:57 +02:00
teddit 9d01c544d1 Merge pull request 'Improve table list' (#245) from arche_dev/teddit:table-patch into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/245
2021-10-04 07:32:28 +02:00
arche_dev d2feeac288 Improve table list
Added I2P Section and moved links to there respective columns.
2021-10-04 03:30:52 +02:00
teddit 8bb949b2b3 redirect http => https before initializing reddit API (fixes #241) 2021-09-19 20:48:51 +02:00
teddit 9e515c4985 fix setting the duration (fixes #240) 2021-09-12 18:03:00 +02:00
teddit 0707349a85 update version 0.4.0 --> 0.4.1 2021-09-09 19:29:05 +02:00
teddit cc9d9e5579 add missing await for processMoreComments 2021-09-09 19:28:02 +02:00
teddit 873e56a23b use async for processMoreComments 2021-09-09 19:19:52 +02:00
teddit a6b9599463 fix including processMoreComments() 2021-09-09 18:25:20 +02:00
teddit 813fb3566c convert processMoreComments to use async function 2021-09-09 18:19:27 +02:00
teddit 4cc8dd7bd5 Merge pull request 'Use async functions' (#239) from json/teddit:async-await into main
Reviewed-on: https://codeberg.org/teddit/teddit/pulls/239
2021-09-09 18:17:00 +02:00
json e1df2ea8ae convert processJsonSubredditsExplore to async function 2021-09-08 13:46:41 +01:00
json 6ced924bc8 remove unused processSubredditsExplore function 2021-09-08 13:46:41 +01:00
json 6ff892767c convert processJsonSubreddit to async function 2021-09-08 13:46:41 +01:00
json 153a50e60a convert processSearchResults to async function 2021-09-08 13:46:41 +01:00
json 0bf3e36c56 remove unused processSearchResults function 2021-09-08 13:46:41 +01:00
json 9bc3ff8d34 remove unused handleTedditApiUser function 2021-09-08 13:46:41 +01:00
json 711f2450c7 change callback to await in processSubredditAbout 2021-09-08 13:46:41 +01:00
json aae3e00ab1 convert processSubredditAbout to async function 2021-09-08 13:46:28 +01:00
json 1c7a3a8f5a separate redis from app.js 2021-09-08 13:40:18 +01:00
json 5f5388da6f remove unused processSubredditAbout function 2021-09-08 13:40:18 +01:00
json d95642cf80 convert processJsonPost to async functions 2021-09-08 13:40:18 +01:00
json 93f70533aa remove unused processJsonPost functions 2021-09-08 13:40:18 +01:00
json f3cc837a01 convert processJsonUser to async function 2021-09-08 13:40:18 +01:00
json d1ef359435 remove unused processJsonUser function 2021-09-07 13:19:28 +01:00
json 7f347d13f7 add prettierrc 2021-09-07 13:11:57 +01:00
teddit 6433afde09 add teddit.pussthecat.org 2021-09-06 18:37:42 +02:00
62 changed files with 4070 additions and 2959 deletions

View File

@ -2,6 +2,12 @@ version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
# Maintain dependencies for npm
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"

View File

@ -10,26 +10,26 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
-
name: Login to GitHub Container Registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Login to DockerHub
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: "Checkout repository"
uses: "actions/checkout@v2"
uses: "actions/checkout@v3"
-
name: Build and push to Docker Hub and Github Packages Docker Registry
id: docker_build
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
push: true
tags: |

View File

@ -9,26 +9,26 @@ jobs:
steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
-
name: Login to GitHub Container Registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Login to DockerHub
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: "Checkout repository"
uses: "actions/checkout@v2"
uses: "actions/checkout@v3"
-
name: Build and push to Docker Hub and Github Packages Docker Registry
id: docker_build
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
push: true
tags: |

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
node_modules/
*.log
config.js
.vscode/

3
.prettierrc Normal file
View File

@ -0,0 +1,3 @@
{
"singleQuote": true
}

View File

@ -1,8 +1,8 @@
# Use LTS Node.js base image
FROM node:14.17-alpine
# Use LTS Node.js slim image
FROM node:slim
# Video support dependency
RUN apk add ffmpeg
RUN apt-get update && apt-get install -y ffmpeg wget
# Install NPM dependencies and copy the project
WORKDIR /teddit
@ -12,4 +12,6 @@ COPY config.js.template ./config.js
RUN find ./static/ -type d -exec chmod -R 777 {} \;
EXPOSE 8080
CMD npm start

142
README.md
View File

@ -1,5 +1,14 @@
# teddit
## teddit is no more actively maintained!
[Due to Reddit's API changes](https://en.wikipedia.org/wiki/2023_Reddit_API_controversy), this project is no more actively maintained.
Feel free to fork the project if you like, or contribute to other alternative Reddit front-ends, such as Libreddit, which are trying to come up with circumventions.
This project is still maintained, but just not actively. You can create PRs, but don't expect them to be merged right away.
---
[teddit.net](https://teddit.net)
A free and open source alternative Reddit front-end focused on privacy.
@ -22,21 +31,37 @@ XMR: 832ogRwuoSs2JGYg7wJTqshidK7dErgNdfpenQ9dzMghNXQTJRby1xGbqC3gW3GAifRM9E84J91
Community instances:
| Instance | Onion Link | Notes |
|-|-|-|
| [teddit.ggc-project.de](https://teddit.ggc-project.de) | | |
| [teddit.kavin.rocks](https://teddit.kavin.rocks) | [teddit4w6cmzmj5kimhfc...onion](http://teddit4w6cmzmj5kimhfcavs7yo5s7alszvsi2khqutqtlaanpcftfyd.onion/) | |
| [teddit.zaggy.nl](https://teddit.zaggy.nl) | | |
| [teddit.namazso.eu](https://teddit.namazso.eu) | | |
| [teddit.nautolan.racing](https://teddit.nautolan.racing) | | |
| [teddit.tinfoil-hat.net](https://teddit.tinfoil-hat.net) | | |
| [teddit.domain.glass](https://teddit.domain.glass) | | |
| [snoo.ioens.is](https://snoo.ioens.is) | [snoo.ioensistjs7wd746...onion](http://snoo.ioensistjs7wd746zluwixvojbbkxhr37lepdvwtdfeav673o64iflqd.onion/) | |
| [teddit.httpjames.space](https://teddit.httpjames.space) | | |
| [ibarajztopxnuhabfu7f...onion](http://ibarajztopxnuhabfu7fg6gbudynxofbnmvis3ltj6lfx47b6fhrd5qd.onion) | | |
| [xugoqcf2pftm76vbznx4...i2p](http://xugoqcf2pftm76vbznx4xuhrzyb5b6zwpizpnw2hysexjdn5l2tq.b32.i2p) | | |
| [teddit.alefvanoon.xyz](https://teddit.alefvanoon.xyz) | | |
| [incogsnoo.com](https://incogsnoo.com) | [tedditfyn6idalzso5wam....onion](tedditfyn6idalzso5wam5qd3kdtxoljjhbrbbx34q2xkcisvshuytad.onion/) | I2P: [http://teddit.i2p](http://teddit.i2p) |
| Instance | Onion Link | I2P | Notes |
|-|-|-|-|
| [teddit.ggc-project.de](https://teddit.ggc-project.de) | | | |
| [teddit.zaggy.nl](https://teddit.zaggy.nl) | | | |
| [teddit.tinfoil-hat.net](https://teddit.tinfoil-hat.net) | | | |
| [teddit.domain.glass](https://teddit.domain.glass) | | | |
| [snoo.ioens.is](https://snoo.ioens.is) | [snoo.ioensistjs7wd746...onion](http://snoo.ioensistjs7wd746zluwixvojbbkxhr37lepdvwtdfeav673o64iflqd.onion/) | | |
| [teddit.httpjames.space](https://teddit.httpjames.space) | | | |
| [teddit.xbdm.fun](https://teddit.xbdm.fun) | | | |
| | [ibarajztopxnuhabfu7f...onion](http://ibarajztopxnuhabfu7fg6gbudynxofbnmvis3ltj6lfx47b6fhrd5qd.onion) | [xugoqcf2pftm76vbznx4...i2p](http://xugoqcf2pftm76vbznx4xuhrzyb5b6zwpizpnw2hysexjdn5l2tq.b32.i2p) | Operated by [mdleom.com](https://mdleom.com/about/#Services) |
| [incogsnoo.com](https://incogsnoo.com) | [tedditfyn6idalzso5wam....onion](http://tedditfyn6idalzso5wam5qd3kdtxoljjhbrbbx34q2xkcisvshuytad.onion/) | [http://teddit.i2p](http://teddit.i2p) | |
| [teddit.pussthecat.org](https://teddit.pussthecat.org) | | | Operated by [PussTheCat.org](https://pussthecat.org/) |
| [reddit.lol](https://reddit.lol) | [http://dawtyi5e2cfyfmoht...onion](http://dawtyi5e2cfyfmoht4izmczi42aa2zwh6wi34zwvc6rzf2acpxhrcrad.onion) | [http://vzeiwzi7ogwl3i...b32.i2p](http://vzeiwzi7ogwl3ijrfek4fbtwhvamxcpyqoc3s4vcgnhlp54s5clq.b32.i2p) | Operated by https://liberta.casa | |
| [teddit.sethforprivacy.com](https://teddit.sethforprivacy.com/) | [qtpvyiaqhmwccx...onion/](http://qtpvyiaqhmwccxwzsqubd23xhmmrt75tdyw35kp43w4hvamsgl3x27ad.onion/) | | For more similar hosted tools, see [blog.sethforprivacy.com](https://blog.sethforprivacy.com/about/#my-community-resources) |
| [teddit.adminforge.de](https://teddit.adminforge.de) | | | Operated by https://adminforge.de |
| [teddit.bus-hit.me](https://teddit.bus-hit.me) | | | Operated by https://bus-hit.me |
| [teddit.froth.zone](https://teddit.froth.zone) | | | |
| [rdt.trom.tf](https://rdt.trom.tf) | | | Part of the https://trom.tf project |
| [teddit.encrypted-data.xyz](https://teddit.encrypted-data.xyz) | | | |
| [i.opnxng.com](https://i.opnxng.com) | | | |
| [teddit.tokhmi.xyz](https://teddit.tokhmi.xyz) | | | |
| [teddit.garudalinux.org](https://teddit.garudalinux.org) | | | Managed by https://garudalinux.org |
| [teddit.privacytools.io](https://teddit.privacytools.io) | [jnuonmf2n36sfdmyksqq....onion](http://jnuonmf2n36sfdmyksqqqyab3w63cq4kx24olyjleh5z6zzfvyt7uqqd.onion) | | Part of [PrivacyTools.io](https://www.privacytools.io/) and hosted by [Privex](https://www.privex.io/) |
| [td.vern.cc](https://td.vern.cc) | [td.vernccvbvyi5qhfzyqen...onion](http://td.vernccvbvyi5qhfzyqengccj7lkove6bjot2xhh5kajhwvidqafczrad.onion) | [td.vern.i2p](http://verncco2oaxjikammz4pi7umzp673cme6zuemx7yeeewspwrw3va.b32.i2p) | Operated by https://vern.cc |
| [teddit.rawbit.ninja](https://teddit.rawbit.ninja) | [yqu4yj5lju7bmlwpzpml...onion](http://yqu4yj5lju7bmlwpzpmltb5gsu6cw7nnbcxxx4iqemwa56nxjiggf4qd.onion) | | Operated by https://rawbit.ninja |
| [teddit.hostux.net](https://teddit.hostux.net) | | | Operated by https://hostux.net |
| [teddit.no-logs.com](https://teddit.no-logs.com/) | | | Operated by https://no-logs.com |
| [teddit.projectsegfau.lt](https://teddit.projectsegfau.lt) | [teddit.pjsfkvpxlinjamta...onion](http://teddit.pjsfkvpxlinjamtawaksbnnaqs2fc2mtvmozrzckxh7f3kis6yea25ad.onion) | | Maintained by Project Segfault Team (https://projectsegfau.lt/team) |
| [teddit.laserdisc.tokyo](https://teddit.laserdisc.tokyo) | | | |
| [t.sneed.network](https://t.sneed.network) | [t.sneed4fmhevap3ci4x...onion](http://t.sneed4fmhevap3ci4xhf4wgkf72lwk275lcgomnfgwniwmqvaxyluuid.onion/) | | Operated by [sneed.network](https://sneed.network) |
| | [teddit.skunky7dhv7no...onion](http://teddit.skunky7dhv7nohsoalpwe3sxfz3fbkad7r3wk632riye25vqm3meqead.onion/) | | Self-hosted, works only from 7-11 AM Moscow time to 10-12 PM, in Donetsk |
<!--
Remove the Changelog section, because the CHANGELOG.md is not updated anymore
@ -47,9 +72,55 @@ See ```CHANGELOG.md```
## Installation
### Docker-compose method
### Docker-compose method (production)
```console
```docker
version: "3.8"
services:
teddit:
container_name: teddit
image: teddit/teddit:latest
environment:
- DOMAIN=teddit.net
- USE_HELMET=true
- USE_HELMET_HSTS=true
- TRUST_PROXY=true
- REDIS_HOST=teddit-redis
ports:
- "127.0.0.1:8080:8080"
networks:
- teddit_net
healthcheck:
test: ["CMD", "wget" ,"--no-verbose", "--tries=1", "--spider", "http://localhost:8080/about"]
interval: 1m
timeout: 3s
depends_on:
- teddit-redis
teddit-redis:
container_name: teddit-redis
image: redis:6.2.5-alpine
command: redis-server
environment:
- REDIS_REPLICATION_MODE=master
networks:
- teddit_net
networks:
teddit_net:
```
Note: This compose is made for a true "production" setup, and is made to be used to have teddit behind a reverse proxy, if you don't want that and prefer to directly access teddit via its port:
- Change `ports: - "127.0.0.1:8080:8080"` to `ports: - "8080:8080"`
- Remove `DOMAIN=teddit.net`, `USE_HELMET=true`, `USE_HELMET_HSTS=true`, `TRUST_PROXY=true`
### Docker-compose method (development)
```bash
git clone https://codeberg.org/teddit/teddit
cd teddit
docker-compose build
@ -73,6 +144,7 @@ The following variables may be set to customize your deployment at runtime.
| flairs_enabled | Enables the rendering of user and link flairs on Teddit. Defaults to **true** |
| highlight_controversial | Enables controversial comments to be indicated by a typographical dagger (†). Defaults to **true** |
| api_enabled | Teddit API feature. Might increase loads significantly on your instance. Defaults to **true** |
| api_force_https | Force HTTPS to Teddit API permalinks (see #285). Defaults to **false** |
| video_enabled | Enables video playback within Teddit. Defaults to **true** |
| redis_enabled | Enables Redis caching. If disabled, does not allow for any caching of Reddit API calls. Defaults to **true** |
| redis_db | Sets the redis DB name, if required |
@ -97,9 +169,9 @@ The following variables may be set to customize your deployment at runtime.
| post_comments_sort | Defines default sort preference. Options are *confidence* (default sorting option in Reddit), *top*, *new*, *controversal*, *old*, *random*, *qa*, *live*. Defaults to **confidence** |
| reddit_app_id | If "use_reddit_oauth" config key is set to true, you have to obtain your Reddit app ID. For testing purposes it's okay to use this project's default app ID. Create your Reddit app here: https://old.reddit.com/prefs/apps/. Make sure to create an "installed app" type of app. Default is **ABfYqdDc9qPh1w** |
| domain_replacements | Replacements for domains in outgoing links. Tuples with regular expressions to match, and replacement values. This is in addition to user-level configuration of privacyDomains. Defaults to **[]** |
| cache_control | *Boolean* If true, teddit will automatically try to keep the size of the cache directory under ```config.cache_max_size```. Defaults to **true** |
| cache_max_size | In Megabytes (MB), how much can we cache media files to the disk? Default is 3000 MB (~3 GB). Note: This is not perfectly exact limit. Defaults to **3000** |
| cache_control_interval | How often the size of the cache directory is checked. Default is every 30 minutes. Defaults to **1000 * 60 * 30** |
| cache_control | *Boolean* If true, teddit will automatically remove all cached static files. Defaults to **true** |
| cache_control_interval | How often the cache directory for static files is emptied (in hours). Default is every 24 hours. Requires cache_control to be true. Defaults to **24** |
| suggested_subreddits | Array of suggested subreddits, which are displayed in the top bar (if the user doesn't have any subscriptions) and in the cleaned home page. Defaults to Reddit's default suggested subreddits. |
### Manual
@ -107,13 +179,13 @@ The following variables may be set to customize your deployment at runtime.
1. (Optional) Install [redis-server](https://redis.io).
Highly recommended  it works as a cache for Reddit API calls.
Highly recommended it works as a cache for Reddit API calls.
1. (Optional) Install [ffmpeg](https://ffmpeg.org).
It's needed if you want to support videos.
```console
```bash
# Linux
apt install redis-server ffmpeg
@ -123,7 +195,7 @@ The following variables may be set to customize your deployment at runtime.
1. Clone and set up the repository.
```console
```bash
git clone https://codeberg.org/teddit/teddit
cd teddit
npm install --no-optional
@ -132,4 +204,26 @@ The following variables may be set to customize your deployment at runtime.
npm start
```
Teddit should now be running at <http://localhost:8080>.
Teddit should now be running at <http://localhost:8080>.
You can also run teddit from a process manager like [pm2](https://www.npmjs.com/package/pm2):
```
## To run:
npm install pm2 -g
pm2 start app.js --name teddit
## To run on startup:
pm2 startup
pm2 save ## if using systemd, see below.
## To restart or stop
pm2 restart teddit
pm2 stop teddit
```
See also the [pm2 instructions for running a project on startup](https://pm2.keymetrics.io/docs/usage/startup/). In particular, if using systemd, see the section on how to modify the systemd init file so that it runs after your system connects to the network.
## Legal
Teddit does not host any content. All content shown on any Teddit instances is from Reddit™. Reddit is a trademark of Reddit Inc. Teddit is not affiliated with Reddit Inc. Any issues with content shown on any Teddit instances need to be reported to Reddit, not the instance host's internet provider or domain provider.

58
app.js
View File

@ -12,41 +12,7 @@ const pug = require('pug');
const compression = require('compression');
const express = require('express');
const cookieParser = require('cookie-parser');
const r = require('redis');
const redis = (() => {
if (!config.redis_enabled) {
// Stub Redis if disabled
return {
get: (_, callback) => callback(null, null),
setex: (_, _1, _2, callback) => callback(null),
on: () => {},
};
}
const redisOptions = {
host: '127.0.0.1',
port: 6379,
};
if (config.redis_db) {
redisOptions.db = config.redis_db;
}
if (config.redis_host) {
redisOptions.host = config.redis_host;
}
if (config.redis_port && config.redis_port > 0) {
redisOptions.port = config.redis_port;
}
if (config.redis_password) {
redisOptions.password = config.redis_password;
}
return r.createClient(redisOptions);
})();
const { redis } = require('./inc/redis');
const nodeFetch = require('node-fetch');
const fetch = config.http_proxy
@ -83,7 +49,7 @@ let https = null;
if (config.https_enabled) {
const privateKey = fs.readFileSync(`${config.cert_dir}/privkey.pem`, 'utf8');
const certificate = fs.readFileSync(`${config.cert_dir}/cert.pem`, 'utf8');
const ca = fs.readFileSync(`${config.cert_dir}/chain.pem`, 'utf8');
const ca = fs.readFileSync(`${config.cert_dir}/fullchain.pem`, 'utf8');
const credentials = {
key: privateKey,
cert: certificate,
@ -140,6 +106,13 @@ app.use(express.static(`${__dirname}/static`));
app.set('views', './views');
app.set('view engine', 'pug');
if (config.redirect_http_to_https) {
app.use((req, res, next) => {
if (req.secure) next();
else res.redirect(`https://${req.headers.host}${req.url}`);
});
}
const redditAPI = require('./inc/initRedditApi.js')(fetch);
/*
@ -155,19 +128,6 @@ app.use('/', allRoutes);
// The old routes
//require('./routes')(app, redis, fetch, redditAPI);
if (config.redirect_http_to_https) {
app.use((req, res, next) => {
if (req.secure) next();
else res.redirect(`https://${req.headers.host}${req.url}`);
});
}
redis.on('error', (error) => {
if (error) {
console.error(`Redis error: ${error}`);
}
});
const cacheControl = require('./cacheControl.js');
cacheControl.removeCacheFiles();

View File

@ -1,66 +1,32 @@
module.exports.removeCacheFiles = function() {
const fs = require('fs')
const config = require('./config')
const pics = './static/pics'
const flairs = './static/pics/flairs'
const icons = './static/pics/icons'
const thumbs = './static/pics/thumbs'
const vids = './static/vids'
let util = require('util')
let spawn = require('child_process').spawn
let usage
const limit = config.cache_max_size
function getUsage() {
return new Promise((resolve, reject) => {
let size = spawn('du', ['-sBM', './static/'])
size.stdout.on('data', function (data) {
data = data.toString()
let lines = data.split('\n')
if(lines) {
for(let i = lines.length; i >= 0; i--) {
if(lines[i] && lines[i].includes('./static/')) {
usage = parseInt(lines[i])
}
}
}
resolve(usage)
})
})
const config = require('./config');
async function deleteStatic() {
const fs = require('fs');
const pics = './static/pics/';
const vids = './static/vids/';
fs.rmdir(pics, { recursive: true, force: true }, () => {
fs.rmdir(vids, { recursive: true, force: true }, () => {
['pics/thumbs', 'pics/flairs', 'pics/icons', 'vids'].map((d) => `./static/${d}`)
.filter((d) => !fs.existsSync(d))
.forEach((d) => fs.mkdirSync(d, { recursive: true }));
console.log('Cleared cached static media files. You can turn this off by setting the config.cache_control to false.');
});
});
}
function deleteFiles() {
return new Promise(async (resolve, reject) => {
usage = await getUsage()
if(usage > limit) {
const { exec } = require('child_process')
exec(`cd ${pics} && ls -1btr -Iflairs -Iicons -Ithumbs -I.gitignore | head -50 | xargs rm -f --`)
exec(`cd ${flairs} && ls -1btr -I.gitignore | head -6 | xargs rm -f --`)
exec(`cd ${icons} && ls -1btr -I.gitignore | head -6 | xargs rm -f --`)
exec(`cd ${thumbs} && ls -1btr -I.gitignore | head -80 | xargs rm -f --`)
exec(`cd ${vids} && ls -1btr -I.gitignore | head -2 | xargs rm -f --`)
}
resolve(1)
})
}
async function main() {
usage = await getUsage()
if(usage > limit) {
console.log('Started removeCacheFiles()')
while(usage > limit) {
await deleteFiles()
}
if(config.cache_control) {
deleteStatic();
let hours = config.cache_control_interval;
if (hours < 1 || hours > 10000 || isNaN(hours)) {
hours = 24;
}
}
if(config.cache_control) {
main()
const interval_ms = config.cache_control_interval
setInterval(() => {
main()
}, interval_ms)
deleteStatic();
}, 1000 * 60 * 60 * hours);
}
}

View File

@ -3,11 +3,13 @@ const config = {
use_reddit_oauth: process.env.USE_REDDIT_OAUTH === 'true' || false, // If false, teddit uses Reddit's public API. If true, you need to have your own Reddit app ID (enter the app ID to the "reddit_app_id" config key).
cert_dir: process.env.CERT_DIR || '', // For example '/home/teddit/letsencrypt/live/teddit.net', if you are using https. No trailing slash.
theme: process.env.THEME || 'auto', // One of: 'dark', 'sepia', 'auto', ''. Auto theme uses browser's theme detection (Dark or White theme). White theme is set by the empty the option ('').
flairs_enabled: process.env.FLAIRS_ENABLED !== 'true' || true, // Enables the rendering of user and link flairs on teddit
highlight_controversial: process.env.HIGHLIGHT_CONTROVERSIAL !== 'true' || true, // Enables controversial comments to be indicated by a typographical dagger (†)
api_enabled: process.env.API_ENABLED !== 'true' || true, // Teddit API feature. Might increase loads significantly on your instance.
video_enabled: process.env.VIDEO_ENABLED !== 'true' || true,
redis_enabled: process.env.REDIS_ENABLED !== 'true' || true, // If disabled, does not cache Reddit API calls
clean_homepage: !('CLEAN_HOMEPAGE' in process.env) || process.env.CLEAN_HOMEPAGE === 'true', // Allows the clean homepage to be used (similar to invidious), instead of the usual reddit-like frontpage
flairs_enabled: !('FLAIRS_ENABLED' in process.env) || process.env.FLAIRS_ENABLED === 'true', // Enables the rendering of user and link flairs on teddit
highlight_controversial: !('HIGHLIGHT_CONTROVERSIAL' in process.env) || process.env.HIGHLIGHT_CONTROVERSIAL === 'true', // Enables controversial comments to be indicated by a typographical dagger (†)
api_enabled: !('API_ENABLED' in process.env) || process.env.API_ENABLED === 'true', // Teddit API feature. Might increase loads significantly on your instance.
api_force_https: process.env.API_FORCE_HTTPS === 'true' || false, // Force HTTPS to Teddit API permalinks (see #285).
video_enabled: !('VIDEO_ENABLED' in process.env) || process.env.VIDEO_ENABLED === 'true',
redis_enabled: !('REDIS_ENABLED' in process.env) || process.env.REDIS_ENABLED === 'true', // If disabled, does not cache Reddit API calls
redis_db: process.env.REDIS_DB,
redis_host: process.env.REDIS_HOST || '127.0.0.1',
redis_password: process.env.REDIS_PASSWORD,
@ -18,23 +20,24 @@ const config = {
https_enabled: process.env.HTTPS_ENABLED === 'true' || false,
redirect_http_to_https: process.env.REDIRECT_HTTP_TO_HTTPS === 'true' || false,
redirect_www: process.env.REDIRECT_WWW === 'true' || false,
use_compression: process.env.USE_COMPRESSION !== 'true' || true,
use_compression: !('USE_COMPRESSION' in process.env) || process.env.USE_COMPRESSION === 'true',
use_view_cache: process.env.USE_VIEW_CACHE === 'true' || false,
use_helmet: process.env.USE_HELMET === 'true' || false, // Recommended to be true when using https
use_helmet_hsts: process.env.USE_HELMET_HSTS === 'true' || false, // Recommended to be true when using https
trust_proxy: process.env.TRUST_PROXY === 'true' || false, // Enable trust_proxy if you are using reverse proxy like nginx
trust_proxy_address: process.env.TRUST_PROXY_ADDRESS || '127.0.0.1',
http_proxy: process.env.HTTP_PROXY,
nsfw_enabled: process.env.NSFW_ENABLED !== 'true' || true, // Enable NSFW (over 18) content. If false, a warning is shown to the user before opening any NSFW post. When the NFSW content is disabled, NSFW posts are hidden from subreddits and from user page feeds. Note: Users can set this to true or false from their preferences.
videos_muted: process.env.VIDEOS_MUTED !== 'true' || true, // Automatically mute all videos in posts
nsfw_enabled: !('NSFW_ENABLED' in process.env) || process.env.NSFW_ENABLED === 'true', // Enable NSFW (over 18) content. If false, a warning is shown to the user before opening any NSFW post. When the NFSW content is disabled, NSFW posts are hidden from subreddits and from user page feeds. Note: Users can set this to true or false from their preferences.
videos_muted: !('VIDEOS_MUTED' in process.env) || process.env.VIDEOS_MUTED === 'true', // Automatically mute all videos in posts
post_comments_sort: process.env.POST_COMMENTS_SORT || 'confidence', // "confidence" is the default sorting in Reddit. Must be one of: confidence, top, new, controversial, old, random, qa, live.
reddit_app_id: process.env.REDDIT_APP_ID || 'ABfYqdDc9qPh1w', // If "use_reddit_oauth" config key is set to true, you have to obtain your Reddit app ID. For testing purposes it's okay to use this project's default app ID. Create your Reddit app here: https://old.reddit.com/prefs/apps/. Make sure to create an "installed app" type of app.
domain_replacements: process.env.DOMAIN_REPLACEMENTS
? (JSON.parse(process.env.DOMAIN_REPLACEMENTS).map(([p, r]) => [new RegExp(p, 'gm'), r]))
: [], // Replacements for domains in outgoing links. Tuples with regular expressions to match, and replacement values. This is in addition to user-level configuration of privacyDomains.
cache_control: process.env.CACHE_CONTROL !== 'true' || true, // If true, teddit will automatically try to keep the size of the cache directory (static) under config.cache_max_size. By default this is set to true.
cache_max_size: process.env.CACHE_MAX_SIZE || 3000, // How much can we cache to the disk? Default is 3000 MB (~3 GB). Note: This is not perfectly exact limit.
cache_control_interval: process.env.CACHE_CONTROL_INTERVAL || 1000 * 60 * 30, // How often the size of the cache directory (static/) is checked. Default is every 30 minutes.
cache_control: !('CACHE_CONTROL' in process.env) || process.env.CACHE_CONTROL === 'true', // If true, teddit will automatically remove all cached static files. By default this is set to true.
cache_control_interval: process.env.CACHE_CONTROL_INTERVAL || 24, // How often the cache directory for static files is emptied (in hours). Requires cache_control to be true. Default is every 24 hours.
show_upvoted_percentage: !('SHOW_UPVOTED_PERCENTAGE' in process.env) || process.env.SHOW_UPVOTED_PERCENTAGE === 'true',
show_upvotes: !('SHOW_UPVOTES' in process.env) || process.env.SHOW_UPVOTES === 'true', // If true, teddit will show number of upvotes in posts and points in comments.
post_media_max_heights: {
/**
* Sets the max-height value for images and videos in posts.
@ -70,9 +73,39 @@ const config = {
initial_limit: 100, // This is the amount of page loads one IP address can make in one minute without getting limited.
limit_after_limited: 30 // When an IP is limited, this is the amount of page loads the IP can make in one minute.
},
valid_media_domains: ['preview.redd.it', 'external-preview.redd.it', 'i.redd.it', 'v.redd.it', 'a.thumbs.redditmedia.com', 'b.thumbs.redditmedia.com', 'emoji.redditmedia.com', 'styles.redditmedia.com', 'www.redditstatic.com', 'thumbs.gfycat.com', 'i.ytimg.com'],
valid_media_domains: process.env.VALID_MEDIA_DOMAINS
? JSON.parse(process.env.VALID_MEDIA_DOMAINS)
: ['preview.redd.it', 'external-preview.redd.it', 'i.redd.it', 'v.redd.it', 'a.thumbs.redditmedia.com', 'b.thumbs.redditmedia.com', 'emoji.redditmedia.com', 'styles.redditmedia.com', 'www.redditstatic.com', 'thumbs.gfycat.com', 'i.ytimg.com', 'i.imgur.com'],
valid_embed_video_domains: ['gfycat.com', 'youtube.com'],
reddit_api_error_text: `Seems like your instance is either blocked (e.g. due to API rate limiting), reddit is currently down, or your API key is expired and not renewd properly. This can also happen for other reasons.`
reddit_api_error_text: `Seems like your instance is either blocked (e.g. due to API rate limiting), reddit is currently down, or your API key is expired and not renewd properly. This can also happen for other reasons.`,
/**
* Here you can configure the suggested subreddits which are visible in the
* cleaned homepage, and in the top bar.
* You should keep at least 'All', and 'Saved'.
*
* If you set your configs with an environment variables for example with
* docker-compose.yml, your suggested_subreddits config could be something
* like this (note the quotes):
* - SUGGESTED_SUBREDDITS=["Popular", "All", "Saved", "selfhosted", "linux", "datahoarder", "Monero"]
* or
* - 'SUGGESTED_SUBREDDITS=["Popular", "All", "Saved", "selfhosted", "linux", "datahoarder", "Monero"]'
*/
suggested_subreddits: process.env.SUGGESTED_SUBREDDITS
? JSON.parse(process.env.SUGGESTED_SUBREDDITS)
:
['Popular', 'All', 'Saved', 'AskReddit', 'pics', 'news',
'worldnews', 'funny', 'tifu', 'videos', 'gaming', 'aww',
'todayilearned', 'gifs', 'Art', 'explainlikeimfive',
'movies', 'Jokes', 'TwoXChromosomes',
'mildlyinteresting', 'LifeProTips', 'askscience',
'IAmA', 'dataisbeautiful', 'books', 'science',
'Showerthoughts', 'gadgets', 'Futurology',
'nottheonion', 'history', 'sports', 'OldSchoolCool',
'GetMotivated', 'DIY', 'photoshopbattles', 'nosleep',
'Music', 'space', 'food', 'UpliftingNews', 'EarthPorn',
'Documentaries', 'InternetIsBeautiful',
'WritingPrompts', 'creepy', 'philosophy',
'announcements', 'listentothis', 'blog'],
};
module.exports = config;

View File

@ -1,21 +1,16 @@
# This docker-compose file is made for development purpose and build from source, if you want to use teddit in production, the README contains a production-ready docker-compose setup.
version: "3.8"
services:
redis:
image: redis:6.2.5-alpine
command: redis-server
environment:
- REDIS_REPLICATION_MODE=master
ports:
- "6379:6379"
networks:
- teddit_net
web:
teddit:
container_name: teddit
build: .
environment:
- REDIS_HOST=redis
- REDIS_HOST=teddit-redis
ports:
- 8080:8080
- "8080:8080"
networks:
- teddit_net
healthcheck:
@ -23,6 +18,16 @@ services:
interval: 1m
timeout: 3s
depends_on:
- redis
- teddit-redis
teddit-redis:
container_name: teddit-redis
image: redis:6.2.5-alpine
command: redis-server
environment:
- REDIS_REPLICATION_MODE=master
networks:
- teddit_net
networks:
teddit_net:

View File

@ -39,8 +39,10 @@ module.exports = function(request, fs) {
this.teddifyUrl = (url, user_preferences) => {
try {
let u = new URL(url)
let domain_replaced = false
if(u.host === 'www.reddit.com' || u.host === 'reddit.com') {
url = url.replace(u.host, config.domain)
domain_replaced = true
if(u.pathname.startsWith('/gallery/'))
url = url.replace('/gallery/', '/comments/')
}
@ -50,10 +52,15 @@ module.exports = function(request, fs) {
let file_ext = getFileExtension(url)
if(image_exts.includes(file_ext))
url = url.replace(`${u.host}/`, `${config.domain}/pics/w:null_`)
domain_replaced = true
if(video_exts.includes(file_ext) || !image_exts.includes(file_ext))
url = url.replace(u.host, `${config.domain}/vids`) + '.mp4'
domain_replaced = true
}
if(domain_replaced && !config.https_enabled) {
url = url.replace('https:', 'http:')
}
} catch(e) { }
url = replaceDomains(url, user_preferences)
return url
@ -182,28 +189,113 @@ module.exports = function(request, fs) {
this.replaceUserDomains = (str, user_preferences) => {
let redditRegex = /([A-z.]+\.)?(reddit(\.com)|redd(\.it))/gm;
let youtubeRegex = /([A-z.]+\.)?youtu(be\.com|\.be)/gm;
let twitterRegex = /([A-z.]+\.)?twitter\.com/gm;
let instagramRegex = /([A-z.]+\.)?instagram.com/gm;
let redditRegex = /(?<=href=")(https?:\/\/)([A-z.]+\.)?(reddit(\.com)|redd(\.it))(?=.+")/gm;
let youtubeRegex = /(?<=href=")(https?:\/\/)([A-z.]+\.)?youtu(be\.com|\.be)(?=.+")/gm;
let twitterRegex = /(?<=href=")(https?:\/\/)(www\.)?twitter\.com(?=.+")/gm;
let instagramRegex = /(?<=href=")(https?:\/\/)(www+\.)?instagram.com(?=.+")/gm;
let quoraRegex = /(?<=href=")(https?:\/\/)([A-z.]+\.)?quora\.com(?=.+")/gm;
str = str.replace(redditRegex, config.domain)
/*
* regex pattern to replace imgur links (imgur.com, imgur.io, i.stack.imgur.com)
* source: https://github.com/libredirect/libredirect/blob/32c4a0211e3b721d46219c05cba93f1a42cf3773/src/config/config.json#L317
* license: GNU GPL v3 License -> https://github.com/libredirect/libredirect/blob/32c4a0211e3b721d46219c05cba93f1a42cf3773/LICENSE
*/
let imgurRegex = /(?<=href=")(https?:\/{2})([im]\.)?(stack\.)?imgur\.(com|io)(?=.+")/gm;
let protocol = config.https_enabled || config.api_force_https ? 'https://' : 'http://'
/**
* Special handling for reddit media domains in comments hrefs or img srcs.
* For example a comment might have a direct links to images in i.redd.it:
* <a href="https://i.redd.it/hly9gyg9gjh81.png">Just refer to this </a>
* We want to rewrite these hrefs, but we also need to include the domain
* for our backend, so we know where to fetch the media from.
* That comment URL then becomes like this after rewriting it:
* <a href="https://teddit.net/hly9gyg9gjh81.png?teddit_proxy=i.redd.it">Just refer to this </a>
* And then in our backend, we check if we have a 'teddit_proxy' in the req
* query, and proceed to proxy if it does.
*/
const replacable_media_domains = ['i.redd.it', 'v.redd.it', 'external-preview.redd.it', 'preview.redd.it']
replacable_media_domains.forEach((domain) => {
if (str.includes(domain + "/")) {
const regex = new RegExp(`(?<=(href|src)=")(https?:\/\/)([A-z.]+\.)?(${domain})(.+?(?="))`, 'gm')
const hrefs = str.match(regex)
if (!hrefs) {
return
}
hrefs.forEach((url) => {
let original_url = url
const valid_exts = ['png', 'jpg', 'jpeg', 'mp4', 'gif', 'gifv']
const file_ext = getFileExtension(url)
if (valid_exts.includes(file_ext)) {
url = url.replace(domain, config.domain)
// append the domain info to the query, for teddit backend
let u = new URL(url)
if (u.search) {
url += '&teddit_proxy=' + domain
} else {
url += '?teddit_proxy=' + domain
}
// also replace the protocol for instances using http only
if (protocol === 'http://' && u.protocol === 'https:') {
url.replace('https://', protocol)
}
str = str.replace(original_url, url)
}
})
}
})
// Continue the normal replace logic
str = str.replace(redditRegex, protocol + config.domain)
if(typeof(user_preferences) == 'undefined')
return str
if(typeof(user_preferences.domain_youtube) != 'undefined')
if(user_preferences.domain_youtube)
str = str.replace(youtubeRegex, user_preferences.domain_youtube)
if(user_preferences.domain_youtube){
if (!youtubeRegex.test(str)){
youtubeRegex = /(https?:\/\/)([A-z.]+\.)?youtu(be\.com|\.be)(?=.+)/gm;
}
str = str.replace(youtubeRegex, protocol + user_preferences.domain_youtube);
}
if(typeof(user_preferences.domain_twitter) != 'undefined')
if(user_preferences.domain_twitter)
str = str.replace(twitterRegex, user_preferences.domain_twitter)
if(typeof(user_preferences.domain_instagram) != 'undefined')
if(user_preferences.domain_instagram)
str = str.replace(instagramRegex, user_preferences.domain_instagram)
if(user_preferences.domain_twitter){
if (!twitterRegex.test(str)){
twitterRegex = /(https?:\/\/)(www\.)?twitter\.com(?=.)/gm;
}
str = str.replace(twitterRegex, protocol + user_preferences.domain_twitter)
}
if(typeof(user_preferences.domain_instagram) != 'undefined'){
if(user_preferences.domain_instagram){
if (!instagramRegex.test(str)){
instagramRegex = /(https?:\/\/)(www+\.)?instagram.com(?=.)/gm;
}
str = str.replace(instagramRegex, protocol + user_preferences.domain_instagram);
}
}
if(typeof(user_preferences.domain_quora) != 'undefined'){
if(user_preferences.domain_quora){
if (!quoraRegex.test(str)){
quoraRegex = /(https?:\/\/)([A-z.]+\.)?quora\.com(?=.)/gm;
}
str = str.replace(quoraRegex, protocol + user_preferences.domain_quora)
}
}
if(typeof(user_preferences.domain_imgur) != 'undefined'){
if(user_preferences.domain_imgur){
if (!imgurRegex.test(str)){
imgurRegex = /(https?:\/{2})([im]\.)?(stack\.)?imgur\.(com|io)(?=.)/gm;
}
str = str.replace(imgurRegex, protocol + user_preferences.domain_imgur)
}
}
return str
}

View File

@ -5,10 +5,14 @@ module.exports = function() {
let comments_html
function commentAuthor(comment, classlist, submitter, moderator) {
let classes = classlist.join(' ')
if (comment.author === '[deleted]')
return `<span class="${classes}">[deleted]</span>`
else
if (comment.author === '[deleted]') {
var reveddit_url = "https://www.reveddit.com" + post_url.substr(post_url.indexOf('/r/')) + comments.id
return `<span class="${classes}"><a href="${reveddit_url}" style='color: #cc6a6a !important;'>[deleted]</a></span>`
}
else {
return `<a href="/u/${comment.author}" class="${classes}">${comment.author}</a>${submitter || ''}${moderator || ''}`
}
}
if(!user_preferences)
@ -115,6 +119,7 @@ module.exports = function() {
}
} else {
let link = comments.parent_id.split('_')[1]
link = post_url + link
comments_html = `
<div class="load-more-comments">
<a href="${link}/#c">continue this thread</a>
@ -205,7 +210,7 @@ module.exports = function() {
}
comments_html += replies_html + '</details></div>'
} else {
if(comment.children.length > 0) {
if(comment.children.length > 0) {
let parent_id = comment.parent_id.split('_')[1]
let load_comms_href = parent_id
@ -215,7 +220,8 @@ module.exports = function() {
</div>
`
} else {
let link = comment.parent_id.split('_')[1]
let link = comment.parent_id.split('_')[1]
link = post_url + link
comments_html = `
<div class="load-more-comments">
<a href="${link}/#c">continue this thread</a>

View File

@ -28,8 +28,14 @@ async function fromJson(data, user_preferences, subreddit_front) {
result.domain = data.domain
result.is_video = data.is_video
result.media = data.media
result.duration = data.is_video ? data.media.reddit_video ? data.media.reddit_video.duration : void 0 : void 0
result.duration = null
result.images = null
if(data.is_video && data.media) {
if(data.media.reddit_video) {
result.duration = data.media.reddit_video.duration
}
}
// Moderation attributes
result.locked = data.locked

View File

@ -30,7 +30,7 @@ module.exports = function(fetch) {
} else {
console.error(`Something went wrong while trying to get an access token from reddit API. ${result.status} ${result.statusText}`)
console.error(reddit_api_error_text)
return res.render('index', { json: null, http_status_code: result.status })
return res.render('frontpage', { json: null, http_status_code: result.status, instance_config: config })
}
}).catch(error => {
console.log(`Error while obtaining a reddit API key.`, error)
@ -66,14 +66,14 @@ module.exports = function(fetch) {
} else {
console.error(`Something went wrong while fetching data from reddit API. ${result.status} ${result.statusText}`)
console.error(reddit_api_error_text)
return res.render('index', { json: null, http_status_code: result.status })
return res.render('frontpage', { json: null, http_status_code: result.status, instance_config: config })
}
}).catch(error => {
console.log(`Error while refreshing the reddit API key.`, error)
})
}
this.redditApiGETHeaders = function() {
let cookies = '_options=%7B%22pref_quarantine_optin%22%3A%20true%7D'
let cookies = `edgebucket=; _options={%22pref_gated_sr_optin%22:true,%22pref_quarantine_optin%22:true}`
if(!config.use_reddit_oauth)
return { headers: { cookie: cookies }, method: 'GET' }

View File

@ -1,155 +1,59 @@
module.exports = function(fetch) {
var compilePostComments = require('./compilePostComments.js')();
var procPostMedia = require('./processPostMedia.js')();
this.processJsonPost = (json, parsed, user_preferences) => {
return new Promise(resolve => {
(async () => {
if(!parsed) {
json = JSON.parse(json)
}
const compilePostComments = require('./compilePostComments.js')();
const procPostMedia = require('./processPostMedia.js')();
const config = require('../config');
let post = json[0].data.children[0].data
let post_id = post.name
let comments = json[1].data.children
async function processReplies(data, post_id, depth, user_preferences) {
let return_replies = [];
for (var i = 0; i < data.length; i++) {
let kind = data[i].kind;
let reply = data[i].data;
let obj = {};
if (kind !== 'more') {
obj = {
author: reply.author,
body_html: reply.body_html,
parent_id: reply.parent_id,
created: reply.created_utc,
edited: reply.edited,
score: reply.score,
ups: reply.ups,
id: reply.id,
permalink: teddifyUrl(reply.permalink),
stickied: reply.stickied,
distinguished: reply.distinguished,
score_hidden: reply.score_hidden,
edited: reply.edited,
replies: [],
depth: depth,
user_flair:
user_preferences.flairs != 'false'
? await formatUserFlair(reply)
: '',
controversiality:
user_preferences.highlight_controversial != 'false'
? reply.controversiality
: '',
};
} else {
obj = {
type: 'load_more',
count: reply.count,
id: reply.id,
parent_id: reply.parent_id,
post_id: post_id,
children: [],
depth: depth,
};
}
let obj = {
author: post.author,
created: post.created_utc,
edited: post.edited,
is_video: post.is_video,
locked: post.locked,
link_flair_text: post.link_flair_text,
name: post_id,
num_comments: post.num_comments,
over_18: post.over_18,
permalink: teddifyUrl(post.permalink),
title: post.title,
url: teddifyUrl(post.url, user_preferences),
ups: post.ups,
id: post.id,
domain: post.domain,
contest_mode: post.contest_mode,
upvote_ratio: post.upvote_ratio,
comments: null,
has_media: false,
media: null,
images: null,
crosspost: false,
selftext: unescape(post.selftext_html),
poll_data: post.poll_data,
link_flair: (user_preferences.flairs != 'false' ? await formatLinkFlair(post) : ''),
user_flair: (user_preferences.flairs != 'false' ? await formatUserFlair(post) : '')
}
if (reply.replies && kind !== 'more') {
if (reply.replies.data.children.length) {
for (var j = 0; j < reply.replies.data.children.length; j++) {
let comment = reply.replies.data.children[j].data;
let objct = {};
let valid_embed_video_domains = ['gfycat.com']
let has_gif = false
let gif_to_mp4 = null
let reddit_video = null
let embed_video = false
if(post.media)
if(valid_embed_video_domains.includes(post.media.type))
embed_video = true
if(post.preview && !embed_video) {
if(post.preview.reddit_video_preview) {
if(post.preview.reddit_video_preview.is_gif) {
has_gif = true
gif_url = post.preview.reddit_video_preview.fallback_url
} else {
let file_ext = getFileExtension(post.preview.reddit_video_preview.fallback_url)
if(file_ext === 'mp4')  {
post.media = true
reddit_video = post.preview.reddit_video_preview
}
}
}
if(post.preview.images) {
if(post.preview.images[0].source) {
let file_ext = getFileExtension(post.preview.images[0].source.url)
if(file_ext === 'gif') {
has_gif = true
let resolutions = post.preview.images[0].variants.mp4.resolutions
gif_to_mp4 = resolutions[resolutions.length - 1]
}
}
}
}
obj = await processPostMedia(obj, post, post.media, has_gif, reddit_video, gif_to_mp4)
if(post.crosspost_parent_list) {
post.crosspost = post.crosspost_parent_list[0]
}
if(post.crosspost) {
obj = await processPostMedia(obj, post.crosspost, post.crosspost.media, has_gif, reddit_video, gif_to_mp4)
obj.crosspost = {
author: post.crosspost.author,
created: post.crosspost.created_utc,
subreddit: post.crosspost.subreddit,
title: post.crosspost.title,
name: post.crosspost.name,
num_comments: post.crosspost.num_comments,
over_18: post.crosspost.over_18,
id: post.crosspost.id,
permalink: teddifyUrl(post.crosspost.permalink),
ups: post.crosspost.ups,
selftext: unescape(post.selftext_html),
selftext_crosspost: unescape(post.crosspost.selftext_html),
poll_data: post.poll_data,
is_crosspost: true,
user_flair: (user_preferences.flairs != 'false' ? await formatUserFlair(post) : '')
}
}
if(post.preview && !obj.has_media) {
obj.images = {
source: await downloadAndSave(post.preview.images[0].source.url)
}
}
if(obj.media) {
if(obj.media.source === 'external') {
if(post.preview) {
obj.images = {
source: await downloadAndSave(post.preview.images[0].source.url)
}
}
}
}
if(post.gallery_data) {
obj.gallery = true
obj.gallery_items = []
for(var i = 0; i < post.gallery_data.items.length; i++) {
let id = post.gallery_data.items[i].media_id
if(post.media_metadata[id]) {
if(post.media_metadata[id].p) {
if(post.media_metadata[id].p[0]) {
let item = { source: null, thumbnail: null, large: null }
if(post.media_metadata[id].s && post.media_metadata[id].p[0].u) {
item = {
type: post.media_metadata[id].e,
source: await downloadAndSave(post.media_metadata[id].s.u),
thumbnail: await downloadAndSave(post.media_metadata[id].p[0].u),
large: await downloadAndSave(post.media_metadata[id].p[post.media_metadata[id].p.length - 1].u),
}
}
obj.gallery_items.push(item)
}
}
}
}
}
let comms = []
for(var i = 0; i < comments.length; i++) {
let comment = comments[i].data
let kind = comments[i].kind
let obj = {}
if(kind !== 'more') {
obj = {
if (comment.author && comment.body_html) {
objct = {
author: comment.author,
body_html: comment.body_html,
parent_id: comment.parent_id,
@ -159,174 +63,478 @@ module.exports = function(fetch) {
ups: comment.ups,
id: comment.id,
permalink: teddifyUrl(comment.permalink),
stickied: comment.stickied,
distinguished: comment.distinguished,
score_hidden: comment.score_hidden,
edited: comment.edited,
distinguished: comment.distinguished,
distinguished: comment.edited,
replies: [],
depth: comment.depth,
user_flair: (user_preferences.flairs != 'false' ? await formatUserFlair(comment) : ''),
controversiality: (user_preferences.highlight_controversial != 'false' ? comment.controversiality : '')
}
depth: depth + 1,
user_flair:
user_preferences.flairs != 'false'
? await formatUserFlair(comment)
: '',
controversiality:
user_preferences.highlight_controversial != 'false'
? comment.controversiality
: '',
};
} else {
obj = {
objct = {
type: 'load_more',
count: comment.count,
id: comment.id,
parent_id: comment.parent_id,
post_id: post.name,
children: []
}
}
if(comment.replies && kind !== 'more') {
if(comment.replies.data) {
if(comment.replies.data.children.length > 0) {
obj.replies = await processReplies(comment.replies.data.children, post_id, 1, user_preferences)
post_id: post_id,
children: [],
depth: depth + 1,
};
if (comment.children) {
for (var k = 0; k < comment.children.length; k++) {
objct.children.push(comment.children[k]);
}
}
}
if(comment.children) {
for(var j = 0; j < comment.children.length; j++) {
obj.children.push(comment.children[j])
if (comment.replies) {
if (comment.replies.data) {
if (comment.replies.data.children.length > 0) {
objct.replies = await processReplies(
comment.replies.data.children,
post_id,
depth,
user_preferences
);
}
}
}
comms.push(obj)
obj.replies.push(objct);
}
obj.comments = comms
resolve(obj)
})()
})
}
this.finalizeJsonPost = async (processed_json, post_id, post_url, morechildren_ids, viewing_comment, user_preferences) => {
let comments_html = `<div class="comments">`
let comments = processed_json.comments
let last_known_depth = undefined
for(var i = 0; i < comments.length; i++) {
let next_comment = false
if(comments[i+1]) {
next_comment = comments[i+1]
}
if(comments[i].depth != undefined) {
last_known_depth = comments[i].depth
}
comments_html += await compilePostCommentsHtml(comments[i], next_comment, post_id, post_url, morechildren_ids, processed_json.author, viewing_comment, user_preferences, last_known_depth)
}
comments_html += `</div>`
delete processed_json['comments']
let post_data = processed_json
return { post_data: post_data, comments: comments_html }
}
this.processReplies = async (data, post_id, depth, user_preferences) => {
let return_replies = []
for(var i = 0; i < data.length; i++) {
let kind = data[i].kind
let reply = data[i].data
let obj = {}
if(kind !== 'more') {
obj = {
author: reply.author,
body_html: reply.body_html,
parent_id: reply.parent_id,
created: reply.created_utc,
edited: reply.edited,
score: reply.score,
ups: reply.ups,
id: reply.id,
permalink: teddifyUrl(reply.permalink),
stickied: reply.stickied,
distinguished: reply.distinguished,
score_hidden: reply.score_hidden,
edited: reply.edited,
replies: [],
depth: depth,
user_flair: (user_preferences.flairs != 'false' ? await formatUserFlair(reply) : ''),
controversiality: (user_preferences.highlight_controversial != 'false' ? reply.controversiality : '')
}
} else {
obj = {
type: 'load_more',
count: reply.count,
id: reply.id,
parent_id: reply.parent_id,
post_id: post_id,
children: [],
depth: depth
}
if (reply.children) {
for (var j = 0; j < reply.children.length; j++) {
obj.children.push(reply.children[j]);
}
if(reply.replies && kind !== 'more') {
if(reply.replies.data.children.length) {
for(var j = 0; j < reply.replies.data.children.length; j++) {
let comment = reply.replies.data.children[j].data
let objct = {}
if(comment.author && comment.body_html) {
objct = {
author: comment.author,
body_html: comment.body_html,
parent_id: comment.parent_id,
created: comment.created_utc,
edited: comment.edited,
score: comment.score,
ups: comment.ups,
id: comment.id,
permalink: teddifyUrl(comment.permalink),
score_hidden: comment.score_hidden,
distinguished: comment.distinguished,
distinguished: comment.edited,
replies: [],
depth: depth + 1,
user_flair: (user_preferences.flairs != 'false' ? await formatUserFlair(comment) : ''),
controversiality: (user_preferences.highlight_controversial != 'false' ? comment.controversiality : '')
}
} else {
objct = {
type: 'load_more',
count: comment.count,
id: comment.id,
parent_id: comment.parent_id,
post_id: post_id,
children: [],
depth: depth + 1
}
if(comment.children) {
for(var k = 0; k < comment.children.length; k++) {
objct.children.push(comment.children[k])
}
}
}
if(comment.replies) {
if(comment.replies.data) {
if(comment.replies.data.children.length > 0) {
objct.replies = await processReplies(comment.replies.data.children, post_id, depth, user_preferences)
}
}
}
obj.replies.push(objct)
}
}
}
if(reply.children) {
for(var j = 0; j < reply.children.length; j++) {
obj.children.push(reply.children[j])
}
}
return_replies.push(obj)
}
return return_replies
return_replies.push(obj);
}
return return_replies;
}
async function processJsonPost(json, parsed, user_preferences) {
if (!parsed) {
json = JSON.parse(json);
}
let post = json[0].data.children[0].data;
let post_id = post.name;
let comments = json[1].data.children;
let obj = {
author: post.author,
created: post.created_utc,
edited: post.edited,
is_video: post.is_video,
locked: post.locked,
link_flair_text: post.link_flair_text,
name: post_id,
num_comments: post.num_comments,
over_18: post.over_18,
permalink: teddifyUrl(post.permalink),
title: post.title,
url: teddifyUrl(post.url, user_preferences),
ups: post.ups,
id: post.id,
domain: post.domain,
contest_mode: post.contest_mode,
upvote_ratio: post.upvote_ratio,
comments: null,
has_media: false,
media: null,
images: null,
crosspost: false,
selftext: unescape(post.selftext_html),
selftext_preview: post.selftext.substr(0, 120).replace(/\n/g, ' '),
poll_data: post.poll_data,
link_flair:
user_preferences.flairs != 'false' ? await formatLinkFlair(post) : '',
user_flair:
user_preferences.flairs != 'false' ? await formatUserFlair(post) : '',
};
let valid_embed_video_domains = ['gfycat.com'];
let has_gif = false;
let gif_to_mp4 = null;
let reddit_video = null;
let embed_video = false;
if (post.media)
if (valid_embed_video_domains.includes(post.media.type)) embed_video = true;
if (post.preview && !embed_video) {
if (post.preview.reddit_video_preview) {
if (post.preview.reddit_video_preview.is_gif) {
has_gif = true;
gif_url = post.preview.reddit_video_preview.fallback_url;
} else {
let file_ext = getFileExtension(
post.preview.reddit_video_preview.fallback_url
);
if (file_ext === 'mp4') {
post.media = true;
reddit_video = post.preview.reddit_video_preview;
}
}
}
if (post.preview.images) {
if (post.preview.images[0].source) {
let file_ext = getFileExtension(post.preview.images[0].source.url);
if (file_ext === 'gif') {
has_gif = true;
let resolutions = post.preview.images[0].variants.mp4.resolutions;
gif_to_mp4 = resolutions[resolutions.length - 1];
}
}
}
}
obj = await processPostMedia(
obj,
post,
post.media,
has_gif,
reddit_video,
gif_to_mp4
);
if (post.crosspost_parent_list) {
post.crosspost = post.crosspost_parent_list[0];
}
if (post.crosspost) {
obj = await processPostMedia(
obj,
post.crosspost,
post.crosspost.media,
has_gif,
reddit_video,
gif_to_mp4
);
obj.crosspost = {
author: post.crosspost.author,
created: post.crosspost.created_utc,
subreddit: post.crosspost.subreddit,
title: post.crosspost.title,
name: post.crosspost.name,
num_comments: post.crosspost.num_comments,
over_18: post.crosspost.over_18,
id: post.crosspost.id,
permalink: teddifyUrl(post.crosspost.permalink),
ups: post.crosspost.ups,
selftext: unescape(post.selftext_html),
selftext_crosspost: unescape(post.crosspost.selftext_html),
poll_data: post.poll_data,
is_crosspost: true,
user_flair:
user_preferences.flairs != 'false' ? await formatUserFlair(post) : '',
};
}
if (post.preview && !obj.has_media) {
obj.images = {
source: await downloadAndSave(post.preview.images[0].source.url),
};
}
if (obj.media) {
if (obj.media.source === 'external') {
if (post.preview) {
obj.images = {
source: await downloadAndSave(post.preview.images[0].source.url),
};
}
}
}
if (post.gallery_data) {
obj.gallery = true;
obj.gallery_items = [];
for (var i = 0; i < post.gallery_data.items.length; i++) {
let id = post.gallery_data.items[i].media_id;
if (post.media_metadata) {
if (post.media_metadata[id]) {
if (post.media_metadata[id].p) {
if (post.media_metadata[id].p[0]) {
let item = { source: null, thumbnail: null, large: null };
if (post.media_metadata[id].s && post.media_metadata[id].p[0].u) {
item = {
type: post.media_metadata[id].e,
source: await downloadAndSave(post.media_metadata[id].s.u),
thumbnail: await downloadAndSave(
post.media_metadata[id].p[0].u
),
large: await downloadAndSave(
post.media_metadata[id].p[
post.media_metadata[id].p.length - 1
].u
),
caption: post.gallery_data.items[i].caption || false,
};
}
obj.gallery_items.push(item);
}
}
}
}
}
}
let comms = [];
for (var i = 0; i < comments.length; i++) {
let comment = comments[i].data;
let kind = comments[i].kind;
let obj = {};
if (kind !== 'more') {
obj = {
author: comment.author,
body_html: comment.body_html,
parent_id: comment.parent_id,
created: comment.created_utc,
edited: comment.edited,
score: comment.score,
ups: comment.ups,
id: comment.id,
permalink: teddifyUrl(comment.permalink),
stickied: comment.stickied,
distinguished: comment.distinguished,
score_hidden: comment.score_hidden,
edited: comment.edited,
replies: [],
depth: comment.depth,
user_flair:
user_preferences.flairs != 'false'
? await formatUserFlair(comment)
: '',
controversiality:
user_preferences.highlight_controversial != 'false'
? comment.controversiality
: '',
};
} else {
obj = {
type: 'load_more',
count: comment.count,
id: comment.id,
parent_id: comment.parent_id,
post_id: post.name,
children: [],
};
}
if (comment.replies && kind !== 'more') {
if (comment.replies.data) {
if (comment.replies.data.children.length > 0) {
obj.replies = await processReplies(
comment.replies.data.children,
post_id,
1,
user_preferences
);
}
}
}
if (comment.children) {
for (var j = 0; j < comment.children.length; j++) {
obj.children.push(comment.children[j]);
}
}
comms.push(obj);
}
obj.comments = comms;
return obj;
}
async function finalizeJsonPost(
processed_json,
post_id,
post_url,
morechildren_ids,
viewing_comment,
user_preferences
) {
let comments_html = `<div class="comments">`;
let comments = processed_json.comments;
let last_known_depth = undefined;
for (var i = 0; i < comments.length; i++) {
let next_comment = false;
if (comments[i + 1]) {
next_comment = comments[i + 1];
}
if (comments[i].depth != undefined) {
last_known_depth = comments[i].depth;
}
comments_html += await compilePostCommentsHtml(
comments[i],
next_comment,
post_id,
post_url,
morechildren_ids,
processed_json.author,
viewing_comment,
user_preferences,
last_known_depth
);
}
comments_html += `</div>`;
delete processed_json['comments'];
let post_data = processed_json;
return { post_data: post_data, comments: comments_html };
}
async function processJsonPostList(posts, mode) {
let protocol = config.https_enabled || config.api_force_https ? 'https' : 'http';
for (var i = 0; i < posts.length; i++) {
let link = posts[i];
let valid_reddit_self_domains = ['reddit.com'];
let is_self_link = false;
if (link.domain) {
let tld = link.domain.split('self.');
if (tld.length > 1) {
if (!tld[1].includes('.')) {
is_self_link = true;
link.url = teddifyUrl(link.url);
}
}
if (
config.valid_media_domains.includes(link.domain) ||
valid_reddit_self_domains.includes(link.domain)
) {
is_self_link = true;
link.url = teddifyUrl(link.url);
}
}
link.permalink = `${protocol}://${config.domain}${link.permalink}`;
if (is_self_link) link.url = link.permalink;
if (link.images) {
if (link.images.thumb !== 'self') {
link.images.thumb = `${protocol}://${config.domain}${link.images.thumb}`;
}
}
if (mode === 'light') {
link.selftext_html = null;
}
}
return posts;
}
async function getPostItem(post_json, req, protocol) {
let thumbnail = '';
let post_image = '';
let is_self_link = false;
let valid_reddit_self_domains = ['reddit.com'];
if (post_json.domain) {
let tld = post_json.domain.split('self.');
if (tld.length > 1) {
if (!tld[1].includes('.')) {
is_self_link = true;
post_json.url = teddifyUrl(post_json.url);
}
}
if (
config.valid_media_domains.includes(post_json.domain) ||
valid_reddit_self_domains.includes(post_json.domain)
) {
is_self_link = true;
post_json.url = teddifyUrl(post_json.url);
}
}
if (post_json.preview && post_json.thumbnail !== 'self') {
if (!post_json.url.startsWith('/r/') && isGif(post_json.url)) {
let s = await downloadAndSave(post_json.thumbnail, 'thumb_');
thumbnail = `${protocol}://${config.domain}${s}`;
} else {
if (post_json.preview.images[0].resolutions[0]) {
let s = await downloadAndSave(
post_json.preview.images[0].resolutions[0].url,
'thumb_'
);
thumbnail = `${protocol}://${config.domain}${s}`;
if (!isGif(post_json.url) && !post_json.post_hint.includes(':video')) {
s = await downloadAndSave(post_json.preview.images[0].source.url);
post_image = `${protocol}://${config.domain}${s}`;
}
}
}
}
post_json.permalink = `${protocol}://${config.domain}${post_json.permalink}`;
if (is_self_link) post_json.url = post_json.permalink;
if (req.query.hasOwnProperty('full_thumbs')) {
if (!post_image) post_image = thumbnail;
thumbnail = post_image;
}
let enclosure = '';
if (thumbnail != '') {
let mime = '';
let ext = thumbnail.split('.').pop();
if (ext === 'png') mime = 'image/png';
else mime = 'image/jpeg';
enclosure = `<enclosure length="0" type="${mime}" url="${thumbnail}" />`;
}
let append_desc_html = `<br/><a href="${post_json.url}">[link]</a> <a href="${post_json.permalink}">[comments]</a>`;
return `
<item>
<title>${post_json.title}</title>
<author>${post_json.author}</author>
<created>${post_json.created}</created>
<pubDate>${new Date(
post_json.created_utc * 1000
).toGMTString()}</pubDate>
<domain>${post_json.domain}</domain>
<id>${post_json.id}</id>
<thumbnail>${thumbnail}</thumbnail>
${enclosure}
<link>${post_json.permalink}</link>
<url>${post_json.url}</url>
<description><![CDATA[${unescape(
post_json.selftext_html
)}${append_desc_html}]]></description>
<num_comments>${post_json.num_comments}</num_comments>
<ups>${post_json.ups}</ups>
<stickied>${post_json.stickied}</stickied>
<is_self_link>${is_self_link}</is_self_link>
</item>
`;
}
module.exports = {
processReplies,
processJsonPost,
finalizeJsonPost,
processJsonPostList,
getPostItem
};

View File

@ -1,83 +1,90 @@
module.exports = function() {
const config = require('../config');
const link = require('./components/link')
this.processJsonSubreddit = (json, from, subreddit_front, user_preferences, saved) => {
return new Promise(resolve => {
(async () => {
if(from === 'redis') {
json = JSON.parse(json)
}
if(json.error) {
resolve({ error: true, error_data: json })
} else {
if(saved) {
let t = {
data: {
before: null,
after: null,
children: json
}
}
json = t
}
let before = json.data.before
let after = json.data.after
const config = require('../config');
const link = require('./components/link');
let ret = {
info: {
before: before,
after: after
},
links: []
}
async function processJsonSubreddit(
json,
from,
subreddit_front,
user_preferences,
saved
) {
if (from === 'redis') {
json = JSON.parse(json);
}
if (json.error) {
return { error: true, error_data: json };
} else {
if (saved) {
let t = {
data: {
before: null,
after: null,
children: json,
},
};
json = t;
}
let children_len = json.data.children.length
let before = json.data.before;
let after = json.data.after;
for(var i = 0; i < children_len; i++) {
let data = json.data.children[i].data
let ret = {
info: {
before: before,
after: after,
},
links: [],
};
if(data.over_18)
if((config.nsfw_enabled === false && user_preferences.nsfw_enabled != 'true') || user_preferences.nsfw_enabled === 'false')
continue
let children_len = json.data.children.length;
/*
// Todo: Remove this once the link component is done
// but keep it for now in case we need it later
let obj = {
author: data.author,
created: data.created_utc,
domain: data.domain,
id: data.id,
images: images,
is_video: data.is_video,
link_flair_text: data.link_flair_text,
locked: data.locked,
media: data.media,
selftext_html: data.selftext_html,
num_comments: data.num_comments,
over_18: data.over_18,
permalink: data.permalink,
score: data.score,
subreddit: data.subreddit,
title: data.title,
ups: data.ups,
upvote_ratio: data.upvote_ratio,
url: replaceDomains(data.url, user_preferences),
stickied: data.stickied,
is_self_link: is_self_link,
subreddit_front: subreddit_front,
link_flair: (user_preferences.flairs != 'false' ? await formatLinkFlair(data) : ''),
user_flair: (user_preferences.flairs != 'false' ? await formatUserFlair(data) : '')
} */
for (var i = 0; i < children_len; i++) {
let data = json.data.children[i].data;
let obj = await link.fromJson(data, user_preferences, subreddit_front)
if (data.over_18)
if (
(config.nsfw_enabled === false &&
user_preferences.nsfw_enabled != 'true') ||
user_preferences.nsfw_enabled === 'false'
)
continue;
ret.links.push(obj)
}
resolve(ret)
}
})()
})
/*
// Todo: Remove this once the link component is done
// but keep it for now in case we need it later
let obj = {
author: data.author,
created: data.created_utc,
domain: data.domain,
id: data.id,
images: images,
is_video: data.is_video,
link_flair_text: data.link_flair_text,
locked: data.locked,
media: data.media,
selftext_html: data.selftext_html,
num_comments: data.num_comments,
over_18: data.over_18,
permalink: data.permalink,
score: data.score,
subreddit: data.subreddit,
title: data.title,
ups: data.ups,
upvote_ratio: data.upvote_ratio,
url: replaceDomains(data.url, user_preferences),
stickied: data.stickied,
is_self_link: is_self_link,
subreddit_front: subreddit_front,
link_flair: (user_preferences.flairs != 'false' ? await formatLinkFlair(data) : ''),
user_flair: (user_preferences.flairs != 'false' ? await formatUserFlair(data) : '')
} */
let obj = await link.fromJson(data, user_preferences, subreddit_front);
ret.links.push(obj);
}
return ret;
}
}
module.exports = processJsonSubreddit;

View File

@ -1,97 +1,138 @@
module.exports = function() {
const config = require('../config');
const link = require('./components/link')
this.processJsonUser = function(json, parsed, after, before, user_preferences, kind, post_type) {
return new Promise(resolve => {
(async () => {
if(!parsed) {
json = JSON.parse(json)
}
const config = require('../config');
const link = require('./components/link');
let about = json.about.data
let posts = []
let view_more_posts = false
let posts_limit = 25
let user_front = false
if(json.overview.data.children.length > posts_limit) {
view_more_posts = true
} else {
posts_limit = json.overview.data.children.length
}
if(!after && !before) {
user_front = true
}
if(json.overview.data.children) {
if(json.overview.data.children[posts_limit - 1]) {
after = json.overview.data.children[posts_limit - 1].data.name
}
if(json.overview.data.children[0]) {
before = json.overview.data.children[0].data.name
}
}
for(var i = 0; i < posts_limit; i++) {
let post = json.overview.data.children[i].data
let thumbnail = 'self'
let type = json.overview.data.children[i].kind
let obj
let post_id = post.permalink.split('/').slice(-2)[0] + '/'
let url = post.permalink.replace(post_id, '')
if(type !== kind && kind)
continue
if(post.over_18)
if((config.nsfw_enabled === false && user_preferences.nsfw_enabled != 'true') || user_preferences.nsfw_enabled === 'false')
continue
if(type === 't3') {
obj = await link.fromJson(post, user_preferences)
obj.type = 't3'
}
if(type === 't1') {
obj = {
type: type,
subreddit: post.subreddit,
title: post.title,
created: post.created_utc,
subreddit_name_prefixed: post.subreddit_name_prefixed,
ups: post.ups,
url: replaceDomains(url, user_preferences),
edited: post.edited,
body_html: unescape(post.body_html),
num_comments: post.num_comments,
over_18: post.over_18,
permalink: post.permalink,
link_author: post.link_author,
link_title: post.link_title,
user_flair: (user_preferences.flairs != 'false' ? await formatUserFlair(post) : '')
}
}
posts.push(obj)
}
let obj = {
username: about.name,
icon_img: await downloadAndSave(about.icon_img, "icon_"),
created: about.created_utc,
verified: about.verified,
link_karma: about.link_karma,
comment_karma: about.comment_karma,
view_more_posts: view_more_posts,
user_front: user_front,
post_type: post_type,
before: before,
after: after,
posts: posts
}
resolve(obj)
})()
})
async function processJsonUser(
json,
parsed,
after,
before,
user_preferences,
kind,
post_type
) {
if (!parsed) {
json = JSON.parse(json);
}
function validateJson(json) {
const empty = {
username: '',
icon_img: '',
created: '',
verified: '',
link_karma: '',
comment_karma: '',
view_more_posts: '',
user_front: '',
post_type:'',
before: '',
after: '',
posts: [],
};
if (!json.overview) {
return { error: true, data: empty };
}
if (!json.overview.data) {
return { error: true, data: empty };
}
return true;
}
const validJson = validateJson(json);
if (validJson.error) {
return validJson.data
}
let about = json.about.data;
let posts = [];
let view_more_posts = false;
let posts_limit = 25;
let user_front = false;
if (json.overview.data.children.length > posts_limit) {
view_more_posts = true;
} else {
posts_limit = json.overview.data.children.length;
}
if (!after && !before) {
user_front = true;
}
if (json.overview.data.children) {
if (json.overview.data.children[posts_limit - 1]) {
after = json.overview.data.children[posts_limit - 1].data.name;
}
if (json.overview.data.children[0]) {
before = json.overview.data.children[0].data.name;
}
}
for (var i = 0; i < posts_limit; i++) {
let post = json.overview.data.children[i].data;
let thumbnail = 'self';
let type = json.overview.data.children[i].kind;
let obj;
let post_id = post.permalink.split('/').slice(-2)[0] + '/';
let url = post.permalink.replace(post_id, '');
if (type !== kind && kind) continue;
if (post.over_18)
if (
(config.nsfw_enabled === false &&
user_preferences.nsfw_enabled != 'true') ||
user_preferences.nsfw_enabled === 'false'
)
continue;
if (type === 't3') {
obj = await link.fromJson(post, user_preferences);
obj.type = 't3';
}
if (type === 't1') {
obj = {
type: type,
subreddit: post.subreddit,
title: post.title,
created: post.created_utc,
subreddit_name_prefixed: post.subreddit_name_prefixed,
ups: post.ups,
url: replaceDomains(url, user_preferences),
edited: post.edited,
body_html: unescape(post.body_html),
num_comments: post.num_comments,
over_18: post.over_18,
permalink: post.permalink,
link_author: post.link_author,
link_title: post.link_title,
user_flair:
user_preferences.flairs != 'false' ? await formatUserFlair(post) : '',
};
}
posts.push(obj);
}
let obj = {
username: about.name,
icon_img: await downloadAndSave(about.icon_img, 'icon_'),
created: about.created_utc,
verified: about.verified,
link_karma: about.link_karma,
comment_karma: about.comment_karma,
view_more_posts: view_more_posts,
user_front: user_front,
post_type: post_type,
before: before,
after: after,
posts: posts,
};
return obj;
}
module.exports = processJsonUser;

View File

@ -1,54 +1,51 @@
module.exports = function() {
const config = require('../config')
this.moreComments = (fetch, redis, post_url, comment_ids, id) => {
return new Promise(resolve => {
(async () => {
let key = `${post_url}:morechildren:comment_ids:${comment_ids}`
redis.get(key, (error, json) => {
if(error) {
console.error(`Error getting the ${key} key from redis (moreComments()).`, error)
resolve(false)
const config = require('../config');
const { redisAsync } = require('./redis');
async function processMoreComments(fetch, redis, post_url, comment_ids, id) {
if (post_url) {
let key = `${post_url}:morechildren:comment_ids:${comment_ids}`
try {
const cached = await redisAsync.get(key);
if (cached !== null) {
return JSON.parse(cached);
}
let url = `https://oauth.reddit.com/api/morechildren?api_type=json&children=${comment_ids}&limit_children=false&link_id=t3_${id}`
const moreCommentsRequest = await fetch(url, redditApiGETHeaders());
if (moreCommentsRequest.ok) {
let response = await moreCommentsRequest.json();
if (response.json.data) {
if (response.json.data.things) {
let comments = response.json.data.things
await redisAsync.setex(
key,
config.setexs.posts,
JSON.stringify(comments)
);
console.log(`Fetched more comments.`);
return comments;
}
if(json) {
json = JSON.parse(json)
resolve(json)
} else {
let url = `https://oauth.reddit.com/api/morechildren?api_type=json&children=${comment_ids}&limit_children=false&link_id=t3_${id}`
fetch(encodeURI(url), redditApiGETHeaders())
.then(result => {
if(result.status === 200) {
result.json()
.then(json => {
if(json.json.data) {
if(json.json.data.things) {
let comments = json.json.data.things
redis.setex(key, config.setexs.posts, JSON.stringify(comments), (error) => {
if(error) {
console.error(`Error setting the ${key} key to redis (moreComments()).`, error)
resolve(false)
} else {
console.log(`Fetched the JSON from Reddit (endpoint "morechildren") for URL: ${post_url}. (moreComments())`)
resolve(comments)
}
})
} else {
resolve(false)
}
} else {
resolve(false)
}
})
} else {
console.error(`Something went wrong while fetching data from Reddit. ${result.status} ${result.statusText} (moreComments())`)
resolve(false)
}
}).catch(error => {
console.log(`Error fetching the JSON from Reddit (endpoint "morechildren") with url: ${url}. (moreComments())`, error)
resolve(false)
})
}
})
})()
})
}
} else {
console.error(
`Something went wrong while fetching data from Reddit:
${moreCommentsRequest.status} ${moreCommentsRequest.statusText}`
);
console.error(config.reddit_api_error_text);
return null;
}
} catch (error) {
console.error('Error fetching more comments: ', error);
return null;
}
} else {
return null;
}
}
module.exports = processMoreComments;

View File

@ -92,12 +92,23 @@ module.exports = function() {
obj.has_media = true
if(!gif_to_mp4) {
if(post.preview) {
obj.media = {
source: await downloadAndSave(post.preview.reddit_video_preview.fallback_url),
height: post.preview.reddit_video_preview.height,
width: post.preview.reddit_video_preview.width,
duration: post.preview.reddit_video_preview.duration,
is_gif: true
if(post.preview.reddit_video_preview) {
const url = post.domain === 'i.imgur.com'
? replaceDomains(post.url_overridden_by_dest.replace(/\.gifv$/, '.mp4'))
: post.preview.reddit_video_preview.fallback_url;
if(url) {
obj.media = {
source: await downloadAndSave(url),
height: post.preview.reddit_video_preview.height,
width: post.preview.reddit_video_preview.width,
duration: post.preview.reddit_video_preview.duration,
is_gif: true
}
} else {
obj.has_media = false
}
} else {
obj.has_media = false
}
} else {
obj.has_media = false
@ -119,12 +130,26 @@ module.exports = function() {
*/
if(!post_media && !has_gif && !post.gallery_data && post.url != '') {
try {
let u = new URL(post.url)
let url = replaceDomains(post.url)
const u = new URL(url)
if(config.valid_media_domains.includes(u.hostname)) {
let ext = u.pathname.split('.')[1]
if(ext === 'jpg' || ext === 'png') {
const ext = u.pathname.split('.')[1]
if(['jpg', 'png', 'jpeg', 'gif'].includes(ext)) {
obj.images = {
source: await downloadAndSave(post.url)
source: await downloadAndSave(url)
}
}
else if(['gifv', 'mp4'].includes(ext)) {
if (obj.domain === 'i.imgur.com') {
url = url.replace(/\.gifv$/, '.mp4');
}
obj.has_media = true
obj.media = {
source: await downloadAndSave(url)
}
if (post.preview && post.preview.images) {
obj.media.height = post.preview.images[0].source.height;
obj.media.width = post.preview.images[0].source.width;
}
}
}

View File

@ -1,62 +1,70 @@
module.exports = function() {
const config = require('../config');
const link = require('./components/link')
this.processSearchResults = (json, parsed, after, before, user_preferences) => {
return new Promise(resolve => {
(async () => {
if(!parsed) {
json = JSON.parse(json)
}
let posts = []
let search_firstpage = false
let before = json.data.before
let after = json.data.after
if(!after && !before) {
search_firstpage = true
}
let suggested_subreddits = false
if(json.suggested_subreddits) {
if(json.suggested_subreddits.data) {
if(json.suggested_subreddits.data.children.length > 0) {
suggested_subreddits = json.suggested_subreddits.data.children
}
}
}
if(json.data.children) {
let view_more_posts = false
let posts_limit = 25
const config = require('../config');
const link = require('./components/link');
if(json.data.children.length > posts_limit) {
view_more_posts = true
} else {
posts_limit = json.data.children.length
}
for(var i = 0; i < posts_limit; i++) {
let post = json.data.children[i].data
if(post.over_18)
if((config.nsfw_enabled === false && user_preferences.nsfw_enabled != 'true') || user_preferences.nsfw_enabled === 'false')
continue
let obj = await link.fromJson(post, user_preferences)
posts.push(obj)
}
}
let obj = {
search_firstpage: search_firstpage,
before: before,
after: after,
posts: posts,
suggested_subreddits: suggested_subreddits,
}
resolve(obj)
})()
})
async function processSearchResults(
json,
parsed,
after,
before,
user_preferences
) {
if (!parsed) {
json = JSON.parse(json);
}
let posts = [];
let search_firstpage = false;
before = json.data.before;
after = json.data.after;
if (!after && !before) {
search_firstpage = true;
}
let suggested_subreddits = false;
if (json.suggested_subreddits) {
if (json.suggested_subreddits.data) {
if (json.suggested_subreddits.data.children.length > 0) {
suggested_subreddits = json.suggested_subreddits.data.children;
}
}
}
if (json.data.children) {
let view_more_posts = false;
let posts_limit = 25;
if (json.data.children.length > posts_limit) {
view_more_posts = true;
} else {
posts_limit = json.data.children.length;
}
for (var i = 0; i < posts_limit; i++) {
let post = json.data.children[i].data;
if (post.over_18)
if (
(config.nsfw_enabled === false &&
user_preferences.nsfw_enabled != 'true') ||
user_preferences.nsfw_enabled === 'false'
)
continue;
let obj = await link.fromJson(post, user_preferences);
posts.push(obj);
}
}
let obj = {
search_firstpage: search_firstpage,
before: before,
after: after,
posts: posts,
suggested_subreddits: suggested_subreddits,
};
return obj;
}
module.exports = processSearchResults;

View File

@ -1,106 +1,78 @@
module.exports = function() {
const config = require('../config')
this.processSubredditAbout = (subreddit, redis, fetch, RedditAPI) => {
return new Promise(resolve => {
(async () => {
if(subreddit && !subreddit.includes('+') && subreddit !== 'all') {
function returnRelevantKeys(json) {
return {
title: json.data.title,
public_description_html: json.data.public_description_html,
active_user_count: json.data.active_user_count,
subscribers: json.data.subscribers,
created_utc: json.data.created_utc,
over18: json.data.over18,
description_html: json.data.description_html,
moderators: json.moderators
}
}
let key = `${subreddit}:sidebar`
redis.get(key, (error, json) => {
if(error) {
console.error(`Error getting the ${subreddit}:sidebar key from redis.`, error)
resolve(null)
}
if(json) {
json = JSON.parse(json)
resolve(returnRelevantKeys(json))
} else {
let url = `https://reddit.com/r/${subreddit}/about.json`
if(config.use_reddit_oauth) {
url = `https://oauth.reddit.com/r/${subreddit}/about`
}
fetch(encodeURI(url), redditApiGETHeaders())
.then(result => {
if(result.status === 200) {
result.json()
.then(json => {
json.moderators = []
redis.setex(key, config.setexs.sidebar, JSON.stringify(json), (error) => {
if(error) {
console.error('Error setting the sidebar key to redis.', error)
return res.render('index', { json: null, user_preferences: req.cookies })
} else {
console.log('Fetched the sidebar from reddit API.')
let moderators_url = `https://reddit.com/r/${subreddit}/about/moderators.json`
if(config.use_reddit_oauth) {
moderators_url = `https://oauth.reddit.com/r/${subreddit}/about/moderators`
}
resolve(returnRelevantKeys(json))
/*
* The following code is commented out because Reddit doesn't
* anymore support fetching moderators for subreddits
* when not logged in.
* This might change in the future though.
* https://codeberg.org/teddit/teddit/issues/207
*/
/*
fetch(encodeURI(moderators_url), redditApiGETHeaders())
.then(mod_result => {
if(mod_result.status === 200) {
mod_result.json()
.then(mod_json => {
json.moderators = mod_json
redis.setex(key, config.setexs.sidebar, JSON.stringify(json), (error) => {
if(error) {
console.error('Error setting the sidebar with moderators key to redis.', error)
return res.render('index', { json: null, user_preferences: req.cookies })
} else {
console.log('Fetched the moderators from reddit API.')
resolve(returnRelevantKeys(json))
}
})
})
} else {
console.error(`Something went wrong while fetching moderators data from reddit API. ${mod_result.status} ${mod_result.statusText}`)
console.error(config.reddit_api_error_text)
resolve(returnRelevantKeys(json))
}
}).catch(error => {
console.error('Error fetching moderators.', error)
resolve(returnRelevantKeys(json))
})
*/
}
})
})
} else {
console.error(`Something went wrong while fetching data from reddit API. ${result.status} ${result.statusText}`)
console.error(config.reddit_api_error_text)
resolve(null)
}
}).catch(error => {
console.error('Error fetching the sidebar.', error)
resolve(null)
})
}
})
} else {
resolve(null)
}
})()
})
const config = require('../config');
const { redisAsync } = require('./redis');
function returnRelevantKeys(json) {
return {
title: json.data.title,
public_description_html: json.data.public_description_html,
active_user_count: json.data.active_user_count,
subscribers: json.data.subscribers,
created_utc: json.data.created_utc,
over18: json.data.over18,
description_html: json.data.description_html,
moderators: json.moderators,
};
}
async function processSubredditAbout(subreddit, redis, fetch, RedditAPI) {
if (subreddit && !subreddit.includes('+') && subreddit !== 'all') {
const key = `${subreddit}:sidebar`;
try {
const cached = await redisAsync.get(key);
if (cached !== null) {
return returnRelevantKeys(JSON.parse(cached));
}
let url = `https://reddit.com/r/${subreddit}/about.json`;
if (config.use_reddit_oauth) {
url = `https://oauth.reddit.com/r/${subreddit}/about`;
}
const subredditAboutRequest = await fetch(url, redditApiGETHeaders());
if (subredditAboutRequest.ok) {
let response = await subredditAboutRequest.json();
response.moderators = [];
await redisAsync.setex(
key,
config.setexs.sidebar,
JSON.stringify(response)
);
console.log(`Fetched sidebar for ${subreddit} from reddit API`);
return returnRelevantKeys(response);
} else {
console.error(
`Something went wrong while fetching data from reddit API:
${subredditAboutRequest.status} ${subredditAboutRequest.statusText}`
);
console.error(config.reddit_api_error_text);
return null;
}
} catch (error) {
console.error('Error fetching the sidebar: ', error);
return null;
}
} else {
return null;
}
}
async function processJsonSubredditAbout(json, parsed) {
if (!parsed) {
json = JSON.parse(json);
}
return returnRelevantKeys(json);
}
module.exports = {
processSubredditAbout,
processJsonSubredditAbout
};

View File

@ -1,52 +1,58 @@
module.exports = function() {
const config = require('../config');
this.processJsonSubredditsExplore = (json, from, subreddit_front, user_preferences) => {
return new Promise(resolve => {
(async () => {
if(from === 'redis') {
json = JSON.parse(json)
}
if(json.error) {
resolve({ error: true, error_data: json })
} else {
let before = json.data.before
let after = json.data.after
const config = require('../config');
let ret = {
info: {
before: before,
after: after
},
links: []
}
async function processJsonSubredditsExplore(
json,
from,
subreddit_front,
user_preferences
) {
if (from === 'redis') {
json = JSON.parse(json);
}
if (json.error) {
return { error: true, error_data: json };
} else {
let before = json.data.before;
let after = json.data.after;
let children_len = json.data.children.length
let ret = {
info: {
before: before,
after: after,
},
links: [],
};
for(var i = 0; i < children_len; i++) {
let data = json.data.children[i].data
if(data.over_18)
if((config.nsfw_enabled === false && user_preferences.nsfw_enabled != 'true') || user_preferences.nsfw_enabled === 'false')
continue
let obj = {
created: data.created_utc,
id: data.id,
over_18: data.over_18,
display_name: data.display_name,
display_name_prefixed: data.display_name_prefixed,
public_description: data.public_description,
url: replaceDomains(data.url, user_preferences),
subscribers: data.subscribers,
over_18: data.over18,
title: data.title,
subreddit_front: subreddit_front,
}
ret.links.push(obj)
}
resolve(ret)
}
})()
})
let children_len = json.data.children.length;
for (var i = 0; i < children_len; i++) {
let data = json.data.children[i].data;
if (data.over_18)
if (
(config.nsfw_enabled === false &&
user_preferences.nsfw_enabled != 'true') ||
user_preferences.nsfw_enabled === 'false'
)
continue;
let obj = {
created: data.created_utc,
id: data.id,
over_18: data.over_18,
display_name: data.display_name,
display_name_prefixed: data.display_name_prefixed,
public_description: data.public_description,
url: replaceDomains(data.url, user_preferences),
subscribers: data.subscribers,
over_18: data.over18,
title: data.title,
subreddit_front: subreddit_front,
};
ret.links.push(obj);
}
return ret;
}
}
module.exports = processJsonSubredditsExplore;

51
inc/redis.js Normal file
View File

@ -0,0 +1,51 @@
const config = require('../config');
const { promisify } = require('util');
const r = require('redis');
const redisOptions = {
host: '127.0.0.1',
port: 6379,
};
if (config.redis_db) {
redisOptions.db = config.redis_db;
}
if (config.redis_host) {
redisOptions.host = config.redis_host;
}
if (config.redis_port && config.redis_port > 0) {
redisOptions.port = config.redis_port;
}
if (config.redis_password) {
redisOptions.password = config.redis_password;
}
// Stub Redis if disabled
const stub = {
get: (_, callback) => callback(null, null),
setex: (_, _1, _2, callback) => callback(null),
on: () => {},
};
const redisDisabled = !config.redis_enabled;
const redis = redisDisabled ? stub : r.createClient(redisOptions);
const redisAsync = {
get: promisify(redis.get).bind(redis),
setex: promisify(redis.setex).bind(redis),
};
redis.on('error', (error) => {
if (error) {
console.error(`Redis error: ${error}`);
}
});
module.exports = {
redis,
redisAsync,
};

View File

@ -0,0 +1,87 @@
const { processJsonPost, getPostItem } = require('../processJsonPost');
module.exports = function () {
const config = require('../../config');
this.handleTedditApiPost = async (
json,
req,
res,
from,
api_type,
api_target
) => {
if (!config.api_enabled) {
res.setHeader('Content-Type', 'application/json');
let msg = {
info: 'This instance do not support API requests. Please see https://codeberg.org/teddit/teddit#instances for instances that support API, or setup your own instance.',
};
return res.end(JSON.stringify(msg));
}
console.log('Teddit API request - post');
if (from === 'redis') json = JSON.parse(json);
if (api_type === 'rss') {
let protocol = config.https_enabled || config.api_force_https ? 'https' : 'http';
let items = '';
let post = json[0].data.children[0].data;
let comments = json[1].data.children;
items += await getPostItem(post, req, protocol);
for (var i = 0; i < comments.length; i++) {
let comment = comments[i].data;
let kind = comments[i].kind;
let title = `/u/${comment.author} on ${post.title}`;
comment.permalink = `${protocol}://${config.domain}${comment.permalink}`;
if (kind !== 'more') {
items += `
<item>
<title>${title}</title>
<author>${comment.author}</author>
<created>${comment.created}</created>
<pubDate>${new Date(
comment.created_utc * 1000
).toGMTString()}</pubDate>
<id>${comment.id}</id>
<link>${comment.permalink}</link>
<description><![CDATA[${unescape(
comment.body_html
)}]]></description>
<ups>${comment.ups}</ups>
</item>
`;
}
}
let xml_output = `<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
<channel>
<atom:link href="${post.permalink}?api&amp;type=rss" rel="self" type="application/rss+xml" />
<title>${post.title}</title>
<link>${post.url}</link>
${items}
</channel>
</rss>`;
res.setHeader('Content-Type', 'application/rss+xml');
return res.end(xml_output);
} else {
res.setHeader('Content-Type', 'application/json');
if (api_target === 'reddit') {
return res.end(JSON.stringify(json));
} else {
let processed_json = await processJsonPost(
json,
true,
req.cookies
);
return res.end(JSON.stringify(processed_json));
}
}
};
};

View File

@ -1,114 +1,52 @@
module.exports = function() {
const config = require('../../config')
this.handleTedditApiSubreddit = async (json, req, res, from, api_type, api_target, subreddit) => {
if(!config.api_enabled) {
res.setHeader('Content-Type', 'application/json')
let msg = { info: 'This instance do not support API requests. Please see https://codeberg.org/teddit/teddit#instances for instances that support API, or setup your own instance.' }
return res.end(JSON.stringify(msg))
}
console.log('Teddit API request - subreddit')
let _json = json // Keep the original json
if(from === 'redis')
json = JSON.parse(json)
if(api_type === 'rss') {
let protocol = (config.https_enabled ? 'https' : 'http')
let items = ''
for(var i = 0; i < json.data.children.length; i++) {
let link = json.data.children[i].data
let thumbnail = ''
let post_image = ''
let is_self_link = false
let valid_reddit_self_domains = ['reddit.com']
const processJsonSubreddit = require('../processJsonSubreddit');
const { processJsonSubredditAbout } = require('../processSubredditAbout');
const processSearchResults = require('../processSearchResults.js');
const { processJsonPostList, getPostItem } = require('../processJsonPost');
const processJsonSubredditsExplore = require('../processSubredditsExplore.js');
if(link.domain) {
let tld = link.domain.split('self.')
if(tld.length > 1) {
if(!tld[1].includes('.')) {
is_self_link = true
link.url = teddifyUrl(link.url)
}
}
if(config.valid_media_domains.includes(link.domain) || valid_reddit_self_domains.includes(link.domain)) {
is_self_link = true
link.url = teddifyUrl(link.url)
}
}
if(link.preview && link.thumbnail !== 'self') {
if(!link.url.startsWith('/r/') && isGif(link.url)) {
let s = await downloadAndSave(link.thumbnail, 'thumb_')
thumbnail = `${protocol}://${config.domain}${s}`
} else {
if(link.preview.images[0].resolutions[0]) {
let s = await downloadAndSave(link.preview.images[0].resolutions[0].url, 'thumb_')
thumbnail = `${protocol}://${config.domain}${s}`
if(!isGif(link.url) && !link.post_hint.includes(':video')) {
s = await downloadAndSave(link.preview.images[0].source.url)
post_image = `${protocol}://${config.domain}${s}`
}
}
}
}
link.permalink = `${protocol}://${config.domain}${link.permalink}`
if(is_self_link)
link.url = link.permalink
if(req.query.hasOwnProperty('full_thumbs')) {
if(!post_image)
post_image = thumbnail
thumbnail = post_image
}
let enclosure = ''
if(thumbnail != '') {
let mime = ''
let ext = thumbnail.split('.').pop()
if(ext === 'png')
mime = 'image/png'
else
mime = 'image/jpeg'
enclosure = `<enclosure length="0" type="${mime}" url="${thumbnail}" />`
}
let append_desc_html = `<br/><a href="${link.url}">[link]</a> <a href="${link.permalink}">[comments]</a>`
items += `
<item>
<title>${link.title}</title>
<author>${link.author}</author>
<created>${link.created}</created>
<pubDate>${new Date(link.created_utc*1000).toGMTString()}</pubDate>
<domain>${link.domain}</domain>
<id>${link.id}</id>
<thumbnail>${thumbnail}</thumbnail>
${enclosure}
<link>${link.permalink}</link>
<url>${link.url}</url>
<description><![CDATA[${unescape(link.selftext_html)}${append_desc_html}]]></description>
<num_comments>${link.num_comments}</num_comments>
<ups>${link.ups}</ups>
<stickied>${link.stickied}</stickied>
<is_self_link>${is_self_link}</is_self_link>
</item>
`
module.exports = function () {
const config = require('../../config');
this.handleTedditApiSubreddit = async (
json,
req,
res,
from,
api_type,
api_target,
subreddit,
mode
) => {
if (!config.api_enabled) {
res.setHeader('Content-Type', 'application/json');
let msg = {
info: 'This instance do not support API requests. Please see https://codeberg.org/teddit/teddit#instances for instances that support API, or setup your own instance.',
};
return res.end(JSON.stringify(msg));
}
console.log('Teddit API request - subreddit');
let _json = json; // Keep the original json
if (from === 'redis') json = JSON.parse(json);
if (api_type === 'rss') {
let protocol = config.https_enabled || config.api_force_https ? 'https' : 'http';
let items = '';
for (var i = 0; i < json.data.children.length; i++) {
let post = json.data.children[i].data;
items += await getPostItem(post, req, protocol);
}
let r_subreddit = '/r/' + subreddit
let title = r_subreddit
let link = `${protocol}://${config.domain}${r_subreddit}`
if(subreddit === '/') {
r_subreddit = 'frontpage'
title = 'teddit frontpage'
link = `${protocol}://${config.domain}`
let r_subreddit = '/r/' + subreddit;
let title = r_subreddit;
let link = `${protocol}://${config.domain}${r_subreddit}`;
if (subreddit === '/') {
r_subreddit = 'frontpage';
title = 'teddit frontpage';
link = `${protocol}://${config.domain}`;
}
let xml_output =
`<?xml version="1.0" encoding="UTF-8"?>
let xml_output = `<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
<channel>
<atom:link href="${link}/?api&amp;type=rss" rel="self" type="application/rss+xml" />
@ -117,50 +55,203 @@ module.exports = function() {
<description>Subreddit feed for: ${r_subreddit}</description>
${items}
</channel>
</rss>`
res.setHeader('Content-Type', 'application/rss+xml')
return res.end(xml_output)
</rss>`;
res.setHeader('Content-Type', 'application/rss+xml');
return res.end(xml_output);
} else {
res.setHeader('Content-Type', 'application/json')
if(api_target === 'reddit') {
return res.end(JSON.stringify(json))
res.setHeader('Content-Type', 'application/json');
if (api_target === 'reddit') {
return res.end(JSON.stringify(json));
} else {
let processed_json = await processJsonSubreddit(_json, from, null, req.cookies)
let protocol = (config.https_enabled ? 'https' : 'http')
for(var i = 0; i < processed_json.links.length; i++) {
let link = processed_json.links[i]
let valid_reddit_self_domains = ['reddit.com']
let is_self_link = false
let processed_json = await processJsonSubreddit(
_json,
from,
null,
req.cookies
);
if(link.domain) {
let tld = link.domain.split('self.')
if(tld.length > 1) {
if(!tld[1].includes('.')) {
is_self_link = true
link.url = teddifyUrl(link.url)
}
}
if(config.valid_media_domains.includes(link.domain) || valid_reddit_self_domains.includes(link.domain)) {
is_self_link = true
link.url = teddifyUrl(link.url)
}
}
link.permalink = `${protocol}://${config.domain}${link.permalink}`
if(is_self_link)
link.url = link.permalink
if(link.images) {
if(link.images.thumb !== 'self') {
link.images.thumb = `${protocol}://${config.domain}${link.images.thumb}`
}
}
}
return res.end(JSON.stringify(processed_json))
}
await processJsonPostList(processed_json.links, mode);
return res.end(JSON.stringify(processed_json));
}
}
}
}
};
this.handleTedditApiSubredditAbout = async (
json,
res,
from,
api_target
) => {
if (!config.api_enabled) {
res.setHeader('Content-Type', 'application/json');
let msg = {
info: 'This instance do not support API requests. Please see https://codeberg.org/teddit/teddit#instances for instances that support API, or setup your own instance.',
};
return res.end(JSON.stringify(msg));
}
console.log('Teddit API request - subreddit about');
if (from === 'redis') json = JSON.parse(json);
res.setHeader('Content-Type', 'application/json');
if (api_target === 'reddit') {
return res.end(JSON.stringify(json));
} else {
let subreddit_about = await processJsonSubredditAbout(
json,
true
);
return res.end(JSON.stringify(subreddit_about));
}
};
this.handleTedditApiSubredditSearch = async (
json,
req,
res,
from,
api_type,
api_target,
subreddit,
query,
mode
) => {
if (!config.api_enabled) {
res.setHeader('Content-Type', 'application/json');
let msg = {
info: 'This instance do not support API requests. Please see https://codeberg.org/teddit/teddit#instances for instances that support API, or setup your own instance.',
};
return res.end(JSON.stringify(msg));
}
console.log('Teddit API request - subreddit search');
if (from === 'redis') json = JSON.parse(json);
if (api_type === 'rss') {
let protocol = config.https_enabled || config.api_force_https ? 'https' : 'http';
let items = '';
for (var i = 0; i < json.data.children.length; i++) {
let post = json.data.children[i].data;
items += await getPostItem(post, req, protocol);
}
let r_subreddit = '/r/' + subreddit;
let title = `${query} - ${r_subreddit}`;
let link = `${protocol}://${config.domain}${r_subreddit}/search?q=${query}`;
let xml_output = `<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
<channel>
<atom:link href="${link}&amp;api&amp;type=rss" rel="self" type="application/rss+xml" />
<title>${title}</title>
<link>${link}</link>
<description>Results for: ${query} - ${r_subreddit}</description>
${items}
</channel>
</rss>`;
res.setHeader('Content-Type', 'application/rss+xml');
return res.end(xml_output);
} else {
res.setHeader('Content-Type', 'application/json');
if (api_target === 'reddit') {
return res.end(JSON.stringify(json));
} else {
let processed_json = await processSearchResults(
json,
true,
null,
null,
req.cookies
);
await processJsonPostList(processed_json.posts, mode);
return res.end(JSON.stringify(processed_json));
}
}
};
this.handleTedditApiSubredditsExplore = async (
json,
req,
res,
from,
api_type,
api_target,
query
) => {
if (!config.api_enabled) {
res.setHeader('Content-Type', 'application/json');
let msg = {
info: 'This instance do not support API requests. Please see https://codeberg.org/teddit/teddit#instances for instances that support API, or setup your own instance.',
};
return res.end(JSON.stringify(msg));
}
console.log('Teddit API request - subreddit explore');
if (from === 'redis') json = JSON.parse(json);
if (api_type === 'rss') {
let protocol = config.https_enabled || config.api_force_https ? 'https' : 'http';
let items = '';
let children_len = json.data.children.length;
for (var i = 0; i < children_len; i++) {
let data = json.data.children[i].data;
items += `
<item>
<title>${data.title}</title>
<created>${data.created}</created>
<pubDate>${new Date(
data.created_utc * 1000
).toGMTString()}</pubDate>
<id>${data.id}</id>
<name>${data.display_name}</name>
<name_prefixed>${data.display_name_prefixed}</name_prefixed>
<url>${replaceDomains(data.url, req.cookies)}</url>
<description><![CDATA[${unescape(
data.public_description_html
)}]]></description>
<subscribers>${data.subscribers}</subscribers>
<over_18>${data.over18}</over_18>
</item>
`;
}
let title = `${query}`;
let link = `${protocol}://${config.domain}/subreddits/search?q=${query}`;
let xml_output = `<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
<channel>
<atom:link href="${link}&amp;api&amp;type=rss" rel="self" type="application/rss+xml" />
<title>${title}</title>
<link>${link}</link>
<description>Results for: ${query}</description>
${items}
</channel>
</rss>`;
res.setHeader('Content-Type', 'application/rss+xml');
return res.end(xml_output);
} else {
res.setHeader('Content-Type', 'application/json');
if (api_target === 'reddit') {
return res.end(JSON.stringify(json));
} else {
let processed_json = await processJsonSubredditsExplore(
json,
true,
null,
req.cookies
);
return res.end(JSON.stringify(processed_json));
}
}
};
};

View File

@ -1,74 +1,86 @@
module.exports = function() {
const config = require('../../config')
this.handleTedditApiUser = async (json, req, res, from, api_type, api_target, user) => {
if(!config.api_enabled) {
res.setHeader('Content-Type', 'application/json')
let msg = { info: 'This instance do not support API requests. Please see https://codeberg.org/teddit/teddit#instances for instances that support API, or setup your own instance.' }
return res.end(JSON.stringify(msg))
const processJsonUser = require('../processJsonUser');
module.exports = function () {
const config = require('../../config');
this.handleTedditApiUser = async (
json,
req,
res,
from,
api_type,
api_target,
user
) => {
if (!config.api_enabled) {
res.setHeader('Content-Type', 'application/json');
let msg = {
info: 'This instance do not support API requests. Please see https://codeberg.org/teddit/teddit#instances for instances that support API, or setup your own instance.',
};
return res.end(JSON.stringify(msg));
}
console.log('Teddit API request - user')
let _json = json // Keep the original json
if(from === 'redis')
json = JSON.parse(json)
let protocol = (config.https_enabled ? 'https' : 'http')
let link = `${protocol}://${config.domain}/user/${user}`
if(api_type === 'rss') {
let items = ''
let posts_limit = 25
if(json.overview.data.children.length <= posts_limit) {
posts_limit = json.overview.data.children.length
console.log('Teddit API request - user');
let _json = json; // Keep the original json
if (from === 'redis') json = JSON.parse(json);
let protocol = config.https_enabled || config.api_force_https ? 'https' : 'http';
let link = `${protocol}://${config.domain}/user/${user}`;
if (api_type === 'rss') {
let items = '';
let posts_limit = 25;
if (json.overview.data.children.length <= posts_limit) {
posts_limit = json.overview.data.children.length;
}
for(var i = 0; i < posts_limit; i++) {
let post = json.overview.data.children[i].data
let post_id = post.permalink.split('/').slice(-2)[0] + '/'
let url = post.permalink.replace(post_id, '')
let permalink = `${protocol}://${config.domain}${post.permalink}`
let comments_url = `${protocol}://${config.domain}${url}`
let kind = json.overview.data.children[i].kind
let t1_elements = ''
let t3_elements = ''
if(kind === 't1') {
let append_desc_html = `<br/><a href="${permalink}">[link]</a> <a href="${comments_url}">[comments]</a>`
for (var i = 0; i < posts_limit; i++) {
let post = json.overview.data.children[i].data;
let post_id = post.permalink.split('/').slice(-2)[0] + '/';
let url = post.permalink.replace(post_id, '');
let permalink = `${protocol}://${config.domain}${post.permalink}`;
let comments_url = `${protocol}://${config.domain}${url}`;
let kind = json.overview.data.children[i].kind;
let t1_elements = '';
let t3_elements = '';
if (kind === 't1') {
let append_desc_html = `<br/><a href="${permalink}">[link]</a> <a href="${comments_url}">[comments]</a>`;
t1_elements = `
<description><![CDATA[${unescape(post.body_html)}${append_desc_html}]]></description>
<description><![CDATA[${unescape(
post.body_html
)}${append_desc_html}]]></description>
<url>${comments_url}</url>
`
`;
}
if(kind === 't3') {
let s = await downloadAndSave(post.thumbnail, 'thumb_')
let thumbnail = ''
let enclosure = ''
if(s !== 'self' && s != '') {
let img = `${protocol}://${config.domain}${s}`
thumbnail = `<thumbnail>${img}</thumbnail>`
let mime = ''
let ext = s.split('.').pop()
if(ext === 'png')
mime = 'image/png'
else
mime = 'image/jpeg'
enclosure = `<enclosure length="0" type="${mime}" url="${img}" />`
if (kind === 't3') {
let s = await downloadAndSave(post.thumbnail, 'thumb_');
let thumbnail = '';
let enclosure = '';
if (s !== 'self' && s != '') {
let img = `${protocol}://${config.domain}${s}`;
thumbnail = `<thumbnail>${img}</thumbnail>`;
let mime = '';
let ext = s.split('.').pop();
if (ext === 'png') mime = 'image/png';
else mime = 'image/jpeg';
enclosure = `<enclosure length="0" type="${mime}" url="${img}" />`;
}
let append_desc_html = `submitted by <a href="${protocol}://${config.domain}/u/${user}>/u/${user}</a> to <a href="${protocol}://${config.domain}/r/${post.subreddit}/">r/${post.subreddit}</a>`
append_desc_html += `<br/><a href="${permalink}">[comments]</a>`
let append_desc_html = `submitted by <a href="${protocol}://${config.domain}/u/${user}>/u/${user}</a> to <a href="${protocol}://${config.domain}/r/${post.subreddit}/">r/${post.subreddit}</a>`;
append_desc_html += `<br/><a href="${permalink}">[comments]</a>`;
t3_elements = `
<description><![CDATA[${unescape(post.selftext_html)}${append_desc_html}]]></description>
<description><![CDATA[${unescape(
post.selftext_html
)}${append_desc_html}]]></description>
${thumbnail}
${enclosure}
`
`;
}
let title = post.title
if(!post.title)
title = post.link_title
let title = post.title;
if (!post.title) title = post.link_title;
items += `
<item>
<title>${title}</title>
@ -76,7 +88,9 @@ module.exports = function() {
<kind>${kind}</kind>
<subreddit>${post.subreddit}</subreddit>
<created>${post.created_utc}</created>
<pubDate>${new Date(post.created_utc*1000).toGMTString()}</pubDate>
<pubDate>${new Date(
post.created_utc * 1000
).toGMTString()}</pubDate>
<ups>${post.ups}</ups>
<link>${permalink}</link>
<edited>${post.edited}</edited>
@ -85,11 +99,10 @@ module.exports = function() {
${t1_elements}
${t3_elements}
</item>
`
`;
}
let xml_output =
`<?xml version="1.0" encoding="UTF-8"?>
let xml_output = `<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
<channel>
<atom:link href="${link}/?api&amp;type=rss" rel="self" type="application/rss+xml" />
@ -97,17 +110,23 @@ module.exports = function() {
<link>${link}</link>
${items}
</channel>
</rss>`
res.setHeader('Content-Type', 'application/rss+xml')
return res.end(xml_output)
</rss>`;
res.setHeader('Content-Type', 'application/rss+xml');
return res.end(xml_output);
} else {
res.setHeader('Content-Type', 'application/json')
if(api_target === 'reddit') {
return res.end(JSON.stringify(json))
res.setHeader('Content-Type', 'application/json');
if (api_target === 'reddit') {
return res.end(JSON.stringify(json));
} else {
let processed_json = await processJsonUser(json, true, null, null, req.cookies)
return res.end(JSON.stringify(processed_json))
}
let processed_json = await processJsonUser(
json,
true,
null,
null,
req.cookies
);
return res.end(JSON.stringify(processed_json));
}
}
}
}
};
};

122
instances.json Normal file
View File

@ -0,0 +1,122 @@
[
{
"url": "https://teddit.net"
},
{
"url": "https://teddit.ggc-project.de"
},
{
"url": "https://teddit.zaggy.nl"
},
{
"url": "https://teddit.tinfoil-hat.net"
},
{
"url": "https://teddit.domain.glass"
},
{
"url": "https://snoo.ioens.is",
"onion": "http://snoo.ioensistjs7wd746zluwixvojbbkxhr37lepdvwtdfeav673o64iflqd.onion"
},
{
"url": "https://teddit.httpjames.space"
},
{
"url": "https://teddit.xbdm.fun"
},
{
"url": "",
"onion": "http://ibarajztopxnuhabfu7fg6gbudynxofbnmvis3ltj6lfx47b6fhrd5qd.onion",
"i2p": "http://xugoqcf2pftm76vbznx4xuhrzyb5b6zwpizpnw2hysexjdn5l2tq.b32.i2p",
"notes": "Operated by mdleom.com"
},
{
"url": "https://incogsnoo.com",
"onion": "http://tedditfyn6idalzso5wam5qd3kdtxoljjhbrbbx34q2xkcisvshuytad.onion",
"i2p": "http://teddit.i2p"
},
{
"url": "https://teddit.pussthecat.org",
"notes": "Operated by PussTheCat.org"
},
{
"url": "https://reddit.lol",
"onion": "http://dawtyi5e2cfyfmoht4izmczi42aa2zwh6wi34zwvc6rzf2acpxhrcrad.onion",
"i2p": "http://vzeiwzi7ogwl3ijrfek4fbtwhvamxcpyqoc3s4vcgnhlp54s5clq.b32.i2p",
"notes": "Operated by https://liberta.casa"
},
{
"url": "https://teddit.sethforprivacy.com",
"onion": "http://qtpvyiaqhmwccxwzsqubd23xhmmrt75tdyw35kp43w4hvamsgl3x27ad.onion",
"notes": "For more similar hosted tools, see blog.sethforprivacy.com"
},
{
"url": "https://teddit.adminforge.de",
"notes": "Operated by https://adminforge.de"
},
{
"url": "https://teddit.bus-hit.me",
"notes": "Operated by https://bus-hit.me"
},
{
"url": "https://teddit.froth.zone"
},
{
"url": "https://rdt.trom.tf/",
"notes": "Part of the https://trom.tf/ project"
},
{
"url": "https://teddit.encrypted-data.xyz"
},
{
"url": "https://i.opnxng.com"
},
{
"url": "https://teddit.tokhmi.xyz"
},
{
"url": "https://teddit.garudalinux.org",
"notes": "Managed by https://garudalinux.org"
},
{
"url": "https://teddit.privacytools.io",
"onion": "http://jnuonmf2n36sfdmyksqqqyab3w63cq4kx24olyjleh5z6zzfvyt7uqqd.onion",
"notes": "Part of: https://www.privacytools.io"
},
{
"url": "https://td.vern.cc",
"onion": "http://td.vernccvbvyi5qhfzyqengccj7lkove6bjot2xhh5kajhwvidqafczrad.onion",
"i2p": "http://verncco2oaxjikammz4pi7umzp673cme6zuemx7yeeewspwrw3va.b32.i2p",
"notes": "Operated by https://vern.cc"
},
{
"url": "https://teddit.rawbit.ninja",
"onion": "http://yqu4yj5lju7bmlwpzpmltb5gsu6cw7nnbcxxx4iqemwa56nxjiggf4qd.onion",
"notes": "Operated by https://rawbit.ninja"
},
{
"url": "https://teddit.hostux.net",
"notes": "Managed by https://hostux.net"
},
{
"url": "https://teddit.no-logs.com/",
"notes": "Managed by https://no-logs.com"
},
{
"url": "https://teddit.projectsegfau.lt",
"onion": "http://teddit.pjsfkvpxlinjamtawaksbnnaqs2fc2mtvmozrzckxh7f3kis6yea25ad.onion",
"notes": "Maintained by Project Segfault Team (https://projectsegfau.lt/team)"
},
{
"url": "https://teddit.laserdisc.tokyo"
},
{
"url": "https://t.sneed.network",
"onion": "http://t.sneed4fmhevap3ci4xhf4wgkf72lwk275lcgomnfgwniwmqvaxyluuid.onion/",
"notes": "Operated by sneednet (https://sneed.network)"
},
{
"onion": "http://teddit.skunky7dhv7nohsoalpwe3sxfz3fbkad7r3wk632riye25vqm3meqead.onion/",
"notes": "Self-hosted, works only from 7-11 AM Moscow time to 10-12 PM, in Donetsk"
}
]

2390
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "teddit",
"version": "0.4.0",
"version": "0.4.9",
"description": "A free and open source alternative Reddit front-end focused on privacy.",
"homepage": "https://teddit.net",
"bugs": {
@ -20,19 +20,22 @@
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node app.js"
"start": "node app.js",
"dev": "nodemon app.js"
},
"dependencies": {
"compression": "^1.7.4",
"cookie-parser": "^1.4.5",
"express": "^4.17.1",
"express": "^4.18.2",
"helmet": "^4.6.0",
"https-proxy-agent": "^5.0.0",
"minizlib": "^2.1.2",
"node-fetch": "^2.6.1",
"node-fetch": "^2.6.5",
"postman-request": "^2.88.1-postman.30",
"pug": "^3.0.2",
"redis": "^3.1.2"
},
"devDependencies": {}
"devDependencies": {
"nodemon": "^3.0.1"
}
}

View File

@ -2,18 +2,25 @@ const config = require('../config');
const { redis, fetch } = require('../app');
const homeRoute = require('express').Router();
const processUser = require('../inc/processJsonUser.js')();
const processPost = require('../inc/processJsonPost.js')();
const processAbout = require('../inc/processSubredditAbout.js')();
const tedditApiUser = require('../inc/teddit_api/handleUser.js')();
const processSearches = require('../inc/processSearchResults.js')();
const processSubreddit = require('../inc/processJsonSubreddit.js')();
const processJsonSubreddit = require('../inc/processJsonSubreddit.js');
const tedditApiSubreddit = require('../inc/teddit_api/handleSubreddit.js')();
const processMoreComments = require('../inc/processMoreComments.js')();
const processSubredditsExplore =
require('../inc/processSubredditsExplore.js')();
homeRoute.get('/:sort?', async (req, res, next) => {
homeRoute.get('/', (req, res, next) => {
if (
(config.clean_homepage && req.cookies.prefer_frontpage !== 'true') ||
(!config.clean_homepage && req.cookies.prefer_frontpage == 'undefined')
) {
return res.render('homepage', {
user_preferences: req.cookies,
instance_config: config,
});
}
next();
});
homeRoute.get([`/:sort?`, '/frontpage'], async (req, res, next) => {
let past = req.query.t;
let before = req.query.before;
let after = req.query.after;
@ -25,26 +32,61 @@ homeRoute.get('/:sort?', async (req, res, next) => {
let proxyable =
sortby.includes('.jpg') ||
sortby.includes('.png') ||
sortby.includes('.jpeg')
sortby.includes('.jpeg') ||
sortby.includes('.mp4') ||
sortby.includes('.gif') ||
sortby.includes('.gifv')
? true
: false;
if (proxyable) {
let params = new URLSearchParams(req.query).toString();
let image_url = `https://preview.redd.it/${sortby}?${params}`;
let proxied_image = await downloadAndSave(image_url);
if (proxied_image) {
return res.redirect(proxied_image);
let media_url = '';
const replacable_media_domains = ['i.redd.it', 'v.redd.it', 'external-preview.redd.it', 'preview.redd.it']
if (req.query.teddit_proxy) {
if (replacable_media_domains.includes(req.query.teddit_proxy)) {
let full_url = req.protocol + '://' + req.get('host') + req.originalUrl;
let u = new URL(full_url);
let filename = u.pathname || '';
let query = u.search || '';
if (query != '') {
let params = new URLSearchParams(query);
params.delete('teddit_proxy');
query = '?' + params.toString();
}
media_url = `https://${req.query.teddit_proxy}${filename}${query}`;
}
} else {
let params = new URLSearchParams(req.query).toString();
media_url = `https://preview.redd.it/${sortby}?${params}`;
if (media_url.includes('teddit_proxy')) {
// if the URL includes teddit_proxy query param, remove everything after it
media_url = media_url.split('%3Fteddit_proxy')[0];
}
}
let proxied_media = await downloadAndSave(media_url);
if (proxied_media) {
return res.redirect(proxied_media);
} else {
return res.redirect('/');
}
}
let is_comment =
(sortby.length == 6 || sortby.length == 7) &&
sortby != "rising"
? true
: false;
if (is_comment) {
return res.redirect('/comments/' + sortby);
}
let d = `&after=${after}`;
if (before) {
d = `&before=${before}`;
}
if (sortby == '') {
if (sortby == '' || sortby == 'frontpage') {
sortby = 'hot';
}
@ -99,9 +141,10 @@ homeRoute.get('/:sort?', async (req, res, next) => {
redis.get(key, (error, json) => {
if (error) {
console.error('Error getting the frontpage key from redis.', error);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
}
if (json) {
@ -115,7 +158,8 @@ homeRoute.get('/:sort?', async (req, res, next) => {
'redis',
api_type,
api_target,
'/'
'/',
'full'
);
} else {
let processed_json = await processJsonSubreddit(
@ -124,12 +168,13 @@ homeRoute.get('/:sort?', async (req, res, next) => {
null,
req.cookies
);
return res.render('index', {
return res.render('frontpage', {
json: processed_json,
sortby: sortby,
past: past,
user_preferences: req.cookies,
redis_key: key,
instance_config: config,
});
}
})();
@ -160,9 +205,10 @@ homeRoute.get('/:sort?', async (req, res, next) => {
'Error setting the frontpage key to redis.',
error
);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
} else {
console.log('Fetched the frontpage from Reddit.');
@ -175,7 +221,8 @@ homeRoute.get('/:sort?', async (req, res, next) => {
'from_online',
api_type,
api_target,
'/'
'/',
'full'
);
} else {
let processed_json = await processJsonSubreddit(
@ -184,12 +231,13 @@ homeRoute.get('/:sort?', async (req, res, next) => {
null,
req.cookies
);
return res.render('index', {
return res.render('frontpage', {
json: processed_json,
sortby: sortby,
past: past,
user_preferences: req.cookies,
redis_key: key,
instance_config: config,
});
}
})();
@ -202,10 +250,11 @@ homeRoute.get('/:sort?', async (req, res, next) => {
`Something went wrong while fetching data from Reddit. ${result.status} ${result.statusText}`
);
console.error(config.reddit_api_error_text);
return res.render('index', {
return res.render('frontpage', {
json: null,
http_status_code: result.status,
user_preferences: req.cookies,
instance_config: config,
});
}
})

View File

@ -68,6 +68,10 @@ overridingRoutes.all('*', (req, res, next) => {
maxAge: 31536000,
httpOnly: true,
});
} else if (!req.cookies.show_upvoted_percentage) {
if (config.show_upvoted_percentage) {
req.cookies.show_upvoted_percentage = 'true';
}
}
let domainTwitter = req.query.domain_twitter;
@ -97,6 +101,24 @@ overridingRoutes.all('*', (req, res, next) => {
});
}
let domainQuora = req.query.domain_quora;
if (domainQuora) {
req.cookies.domain_quora = domainQuora;
res.cookie('domain_quora', domainQuora, {
maxAge: 31536000,
httpOnly: true,
});
}
let domainImgur = req.query.domain_imgur;
if (domainImgur) {
req.cookies.domain_imgur = domainImgur;
res.cookie('domain_imgur', domainImgur, {
maxAge: 31536000,
httpOnly: true,
});
}
let videosMuted = req.query.videos_muted;
if (videosMuted) {
req.cookies.videos_muted = videosMuted;

View File

@ -10,17 +10,24 @@ function resetPreferences(res) {
res.clearCookie('post_media_max_height');
res.clearCookie('collapse_child_comments');
res.clearCookie('show_upvoted_percentage');
res.clearCookie('show_upvotes')
res.clearCookie('subbed_subreddits');
res.clearCookie('domain_twitter');
res.clearCookie('domain_youtube');
res.clearCookie('domain_instagram');
res.clearCookie('domain_quora');
res.clearCookie('domain_imgur');
res.clearCookie('videos_muted');
res.clearCookie('prefer_frontpage');
res.clearCookie('show_large_gallery_images');
res.clearCookie('default_comment_sort');
}
preferenceRoutes.get('/preferences', (req, res, next) => {
return res.render('preferences', {
user_preferences: req.cookies,
instance_config: config,
comment_sort_values: ['best', 'top', 'new', 'controversial', 'old', 'qa'],
});
});
@ -41,9 +48,10 @@ preferenceRoutes.get('/import_prefs/:key', (req, res, next) => {
`Error getting the preferences import key ${key} from redis.`,
error
);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
}
if (json) {
@ -82,10 +90,16 @@ preferenceRoutes.post('/saveprefs', (req, res, next) => {
let post_media_max_height = req.body.post_media_max_height;
let collapse_child_comments = req.body.collapse_child_comments;
let show_upvoted_percentage = req.body.show_upvoted_percentage;
let show_upvotes = req.body.show_upvotes;
let domain_twitter = req.body.domain_twitter;
let domain_youtube = req.body.domain_youtube;
let domain_instagram = req.body.domain_instagram;
let domain_quora = req.body.domain_quora;
let domain_imgur = req.body.domain_imgur;
let videos_muted = req.body.videos_muted;
let prefer_frontpage = req.body.prefer_frontpage;
let show_large_gallery_images = req.body.show_large_gallery_images;
let default_comment_sort = req.body.default_comment_sort;
res.cookie('theme', theme, {
maxAge: 365 * 24 * 60 * 60 * 1000,
@ -136,6 +150,13 @@ preferenceRoutes.post('/saveprefs', (req, res, next) => {
httpOnly: true,
});
if (show_upvotes === 'on') show_upvotes = 'true';
else show_upvotes = 'false';
res.cookie('show_upvotes', show_upvotes, {
maxAge: 365 * 24 * 60 * 60 * 1000,
httpOnly: true,
});
if (videos_muted === 'on') videos_muted = 'true';
else videos_muted = 'false';
res.cookie('videos_muted', videos_muted, {
@ -155,6 +176,33 @@ preferenceRoutes.post('/saveprefs', (req, res, next) => {
maxAge: 365 * 24 * 60 * 60 * 1000,
httpOnly: true,
});
res.cookie('domain_quora', domain_quora, {
maxAge: 365 * 24 * 60 * 60 * 1000,
httpOnly: true,
});
res.cookie('domain_imgur', domain_imgur, {
maxAge: 365 * 24 * 60 * 60 * 1000,
httpOnly: true,
});
if (prefer_frontpage === 'on') prefer_frontpage = 'true';
else prefer_frontpage = 'false';
res.cookie('prefer_frontpage', prefer_frontpage, {
maxAge: 365 * 24 * 60 * 60 * 1000,
httpOnly: true,
});
if (show_large_gallery_images === 'on') show_large_gallery_images = 'true';
else show_large_gallery_images = 'false';
res.cookie('show_large_gallery_images', show_large_gallery_images, {
maxAge: 365 * 24 * 60 * 60 * 1000,
httpOnly: true,
});
res.cookie('default_comment_sort', default_comment_sort, {
maxAge: 365 * 24 * 60 * 60 * 1000,
httpOnly: true,
});
return res.redirect('/preferences');
});
@ -173,7 +221,7 @@ preferenceRoutes.post('/export_prefs', (req, res, next) => {
'Content-disposition',
'attachment; filename=teddit_prefs.json'
);
res.setHeader('Content-type', 'preferenceRouteslication/json');
res.setHeader('Content-type', 'application/json');
return res.send(export_data);
}
@ -204,7 +252,7 @@ preferenceRoutes.post('/import_prefs', (req, res, next) => {
body = body.toString();
try {
let json = body
.split('Content-Type: preferenceRouteslication/json')[1]
.split('Content-Type: application/json')[1]
.trim()
.split('--')[0];
let prefs = JSON.parse(json);

View File

@ -2,16 +2,9 @@ const config = require('../config');
const { redis, fetch } = require('../app');
const saveRoutes = require('express').Router();
const processUser = require('../inc/processJsonUser.js')();
const processPost = require('../inc/processJsonPost.js')();
const processAbout = require('../inc/processSubredditAbout.js')();
const tedditApiUser = require('../inc/teddit_api/handleUser.js')();
const processSearches = require('../inc/processSearchResults.js')();
const processSubreddit = require('../inc/processJsonSubreddit.js')();
const processJsonSubreddit = require('../inc/processJsonSubreddit.js');
const tedditApiSubreddit = require('../inc/teddit_api/handleSubreddit.js')();
const processMoreComments = require('../inc/processMoreComments.js')();
const processSubredditsExplore =
require('../inc/processSubredditsExplore.js')();
saveRoutes.get('/saved', (req, res, next) => {
let saved = req.cookies.saved;
@ -20,6 +13,7 @@ saveRoutes.get('/saved', (req, res, next) => {
return res.render('saved', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
}
@ -45,6 +39,7 @@ saveRoutes.get('/saved', (req, res, next) => {
return res.render('saved', {
json: processed_json,
user_preferences: req.cookies,
instance_config: config,
});
} else {
return res.render('subreddit', {
@ -52,6 +47,7 @@ saveRoutes.get('/saved', (req, res, next) => {
error: true,
data: processed_json,
user_preferences: req.cookies,
instance_config: config,
});
}
})();
@ -221,9 +217,10 @@ saveRoutes.get(
'Error getting the short URL for post key from redis.',
error
);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
}
if (json) {
@ -264,9 +261,10 @@ saveRoutes.get(
'Error setting the short URL for post key to redis.',
error
);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
} else {
console.log(
@ -294,10 +292,11 @@ saveRoutes.get(
`Something went wrong while fetching data from Reddit. ${result.status} ${result.statusText}`
);
console.error(config.reddit_api_error_text);
return res.render('index', {
return res.render('frontpage', {
json: null,
http_status_code: result.status,
user_preferences: req.cookies,
instance_config: config,
});
}
})

View File

@ -1,3 +1,4 @@
const config = require('../config');
const searchRoute = require('express').Router();
searchRoute.get('/search', (req, res, next) => {
@ -14,6 +15,7 @@ searchRoute.get('/search', (req, res, next) => {
sortby: undefined,
past: undefined,
user_preferences: req.cookies,
instance_config: config,
});
}

View File

@ -1,11 +1,13 @@
const config = require('../config');
const staticRoutes = require('express').Router();
staticRoutes.get('/privacy', (req, res, next) => {
return res.render('privacypolicy', { user_preferences: req.cookies });
return res.render('privacypolicy', { user_preferences: req.cookies, instance_config: config });
});
staticRoutes.get('/about', (req, res, next) => {
return res.render('about', { user_preferences: req.cookies });
return res.render('about', { user_preferences: req.cookies, instance_config: config });
});
module.exports = staticRoutes;

View File

@ -2,20 +2,32 @@ const config = require('../config');
const { redis, fetch, RedditAPI } = require('../app');
const subredditRoutes = require('express').Router();
const processUser = require('../inc/processJsonUser.js')();
const processPost = require('../inc/processJsonPost.js')();
const processAbout = require('../inc/processSubredditAbout.js')();
const tedditApiUser = require('../inc/teddit_api/handleUser.js')();
const processSearches = require('../inc/processSearchResults.js')();
const processSubreddit = require('../inc/processJsonSubreddit.js')();
const {
processJsonPost,
finalizeJsonPost,
} = require('../inc/processJsonPost.js');
const {
processSubredditAbout
} = require('../inc/processSubredditAbout.js');
const processSearchResults = require('../inc/processSearchResults.js');
const processJsonSubreddit = require('../inc/processJsonSubreddit.js');
const tedditApiSubreddit = require('../inc/teddit_api/handleSubreddit.js')();
const processMoreComments = require('../inc/processMoreComments.js')();
const processSubredditsExplore =
require('../inc/processSubredditsExplore.js')();
const tedditApiPost = require('../inc/teddit_api/handlePost.js')();
const processMoreComments = require('../inc/processMoreComments.js');
const processJsonSubredditsExplore = require('../inc/processSubredditsExplore.js');
subredditRoutes.get('/r/:subreddit/search', (req, res, next) => {
let subreddit = req.params.subreddit;
let q = req.query.q;
let api_req = req.query.api;
let api_type = req.query.type;
let api_target = req.query.target;
let api_mode = req.query.mode;
if (req.query.hasOwnProperty('api')) api_req = true;
else api_req = false;
let raw_json = api_req && req.query.raw_json == '1' ? 1 : 0;
if (typeof q === 'undefined') {
return res.render('search', {
@ -28,6 +40,7 @@ subredditRoutes.get('/r/:subreddit/search', (req, res, next) => {
sortby: undefined,
past: undefined,
user_preferences: req.cookies,
instance_config: config,
});
}
@ -55,44 +68,65 @@ subredditRoutes.get('/r/:subreddit/search', (req, res, next) => {
if (nsfw !== 'on') {
nsfw = 'off';
}
let count = '&count=25';
if (after == '') {
count = '';
}
let key = `search:${subreddit}:${q}:${restrict_sr}:${sortby}:${past}:${after}:${before}:${nsfw}`;
let key = `search:${subreddit}:${q}:${restrict_sr}:${sortby}:${past}:${after}:${before}:${nsfw}:raw_json:${raw_json}`;
redis.get(key, (error, json) => {
if (error) {
console.error('Error getting the search key from redis.', error);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
}
if (json) {
console.log('Got search key from redis.');
(async () => {
let processed_json = await processSearchResults(
json,
false,
after,
before,
req.cookies
);
return res.render('search', {
json: processed_json,
no_query: false,
q: q,
restrict_sr: restrict_sr,
nsfw: nsfw,
subreddit: subreddit,
sortby: sortby,
past: past,
user_preferences: req.cookies,
});
if (api_req) {
return handleTedditApiSubredditSearch(
json,
req,
res,
'redis',
api_type,
api_target,
subreddit,
q,
api_mode
);
} else {
let processed_json = await processSearchResults(
json,
false,
after,
before,
req.cookies
);
return res.render('search', {
json: processed_json,
no_query: false,
q: q,
restrict_sr: restrict_sr,
nsfw: nsfw,
subreddit: subreddit,
sortby: sortby,
past: past,
user_preferences: req.cookies,
instance_config: config,
});
}
})();
} else {
let url = '';
if (config.use_reddit_oauth)
url = `https://oauth.reddit.com/r/${subreddit}/search?api_type=json&q=${q}&restrict_sr=${restrict_sr}&include_over_18=${nsfw}&sort=${sortby}&t=${past}${d}`;
url = `https://oauth.reddit.com/r/${subreddit}/search?api_type=json&q=${q}&restrict_sr=${restrict_sr}&include_over_18=${nsfw}&sort=${sortby}&t=${past}${count}${d}&raw_json=${raw_json}`;
else
url = `https://reddit.com/r/${subreddit}/search.json?api_type=json&q=${q}&restrict_sr=${restrict_sr}&include_over_18=${nsfw}&sort=${sortby}&t=${past}${d}`;
url = `https://reddit.com/r/${subreddit}/search.json?api_type=json&q=${q}&restrict_sr=${restrict_sr}&include_over_18=${nsfw}&sort=${sortby}&t=${past}${count}${d}&raw_json=${raw_json}`;
fetch(encodeURI(url), redditApiGETHeaders())
.then((result) => {
if (result.status === 200) {
@ -121,31 +155,47 @@ subredditRoutes.get('/r/:subreddit/search', (req, res, next) => {
'Error setting the searches key to redis.',
error
);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
} else {
console.log('Fetched search results from Reddit.');
(async () => {
let processed_json = await processSearchResults(
json,
true,
after,
before,
req.cookies
);
return res.render('search', {
no_query: false,
json: processed_json,
q: q,
restrict_sr: restrict_sr,
nsfw: nsfw,
subreddit: subreddit,
sortby: sortby,
past: past,
user_preferences: req.cookies,
});
if (api_req) {
return handleTedditApiSubredditSearch(
json,
req,
res,
'from_online',
api_type,
api_target,
subreddit,
q,
api_mode
);
} else {
let processed_json = await processSearchResults(
json,
true,
after,
before,
req.cookies
);
return res.render('search', {
no_query: false,
json: processed_json,
q: q,
restrict_sr: restrict_sr,
nsfw: nsfw,
subreddit: subreddit,
sortby: sortby,
past: past,
user_preferences: req.cookies,
instance_config: config,
});
}
})();
}
}
@ -157,10 +207,11 @@ subredditRoutes.get('/r/:subreddit/search', (req, res, next) => {
`Something went wrong while fetching data from Reddit. ${result.status} ${result.statusText}`
);
console.error(config.reddit_api_error_text);
return res.render('index', {
return res.render('frontpage', {
json: null,
http_status_code: result.status,
user_preferences: req.cookies,
instance_config: config,
});
}
})
@ -171,6 +222,107 @@ subredditRoutes.get('/r/:subreddit/search', (req, res, next) => {
});
});
subredditRoutes.get('/r/:subreddit/about', (req, res, next) => {
let subreddit = req.params.subreddit;
let api_type = req.query.type;
let api_target = req.query.target;
let api_mode = req.query.mode;
if (!req.query.hasOwnProperty('api')) {
console.log(`This route is only available via the API.`, req.originalUrl);
return res.redirect(`/r/${subreddit}`);
}
let raw_json = req.query.raw_json == '1' ? 1 : 0;
let key = `about:${subreddit.toLowerCase()}:raw_json:${raw_json}`;
redis.get(key, (error, json) => {
if (error) {
console.error(`Error getting the about key from redis.`, error);
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
}
if (json) {
console.log(`Got about key from redis.`);
(async () => {
return handleTedditApiSubredditAbout(
json,
res,
'redis',
api_target
);
})();
} else {
let url = '';
if (config.use_reddit_oauth)
url = `https://oauth.reddit.com/r/${subreddit}/about.json?api_type=json&raw_json=${raw_json}`;
else
url = `https://reddit.com/r/${subreddit}/about.json?api_type=json&raw_json=${raw_json}`;
fetch(encodeURI(url), redditApiGETHeaders())
.then((result) => {
if (result.status === 200) {
result.json().then((json) => {
redis.setex(
key,
config.setexs.subreddit,
JSON.stringify(json),
(error) => {
if (error) {
console.error(
`Error setting the about key to redis.`,
error
);
return res.render('subreddit', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
} else {
console.log(
`Fetched the JSON from reddit.com/r/${subreddit}/about.`
);
(async () => {
return handleTedditApiSubredditAbout(
json,
res,
'from_online',
api_target
);
})();
}
}
);
});
} else {
if (result.status === 404) {
console.log('404 Subreddit not found');
} else {
console.error(
`Something went wrong while fetching data from Reddit. ${result.status} ${result.statusText}`
);
console.error(config.reddit_api_error_text);
}
return res.render('frontpage', {
json: null,
http_status_code: result.status,
user_preferences: req.cookies,
instance_config: config,
});
}
})
.catch((error) => {
console.error(
`Error fetching the JSON file from reddit.com/r/${subreddit}/about.`,
error
);
});
}
});
});
subredditRoutes.get(
'/r/:subreddit/wiki/:page?/:sub_page?',
(req, res, next) => {
@ -201,9 +353,10 @@ subredditRoutes.get(
`Error getting the ${subreddit} wiki key from redis.`,
error
);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
}
if (json) {
@ -216,6 +369,7 @@ subredditRoutes.get(
: formatWikipagelisting(json, subreddit),
subreddit: subreddit,
user_preferences: req.cookies,
instance_config: config,
});
} else {
let url = '';
@ -240,6 +394,7 @@ subredditRoutes.get(
return res.render('subreddit', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
} else {
console.log(
@ -252,6 +407,7 @@ subredditRoutes.get(
: formatWikipagelisting(json, subreddit),
subreddit: subreddit,
user_preferences: req.cookies,
instance_config: config,
});
}
}
@ -266,10 +422,11 @@ subredditRoutes.get(
);
console.error(config.reddit_api_error_text);
}
return res.render('index', {
return res.render('frontpage', {
json: null,
http_status_code: result.status,
user_preferences: req.cookies,
instance_config: config,
});
}
})
@ -323,6 +480,7 @@ subredditRoutes.get('/r/random', (req, res, next) => {
return res.render('subreddit', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
} else {
console.log(
@ -334,9 +492,10 @@ subredditRoutes.get('/r/random', (req, res, next) => {
);
} else {
console.error(`Fetching random subreddit failed.`, json);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
}
});
@ -349,10 +508,11 @@ subredditRoutes.get('/r/random', (req, res, next) => {
);
console.error(config.reddit_api_error_text);
}
return res.render('index', {
return res.render('frontpage', {
json: null,
http_status_code: result.status,
user_preferences: req.cookies,
instance_config: config,
});
}
})
@ -373,6 +533,7 @@ subredditRoutes.get('/r/:subreddit/:sort?', (req, res, next) => {
let api_req = req.query.api;
let api_type = req.query.type;
let api_target = req.query.target;
let api_mode = req.query.mode;
if (req.query.hasOwnProperty('api')) api_req = true;
else api_req = false;
@ -414,9 +575,10 @@ subredditRoutes.get('/r/:subreddit/:sort?', (req, res, next) => {
redis.get(key, (error, json) => {
if (error) {
console.error(`Error getting the ${subreddit} key from redis.`, error);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
}
if (json) {
@ -430,7 +592,8 @@ subredditRoutes.get('/r/:subreddit/:sort?', (req, res, next) => {
'redis',
api_type,
api_target,
subreddit
subreddit,
api_mode
);
} else {
let processed_json = await processJsonSubreddit(
@ -458,6 +621,7 @@ subredditRoutes.get('/r/:subreddit/:sort?', (req, res, next) => {
redis_key: key,
after: req.query.after,
before: req.query.before,
instance_config: config,
});
} else {
return res.render('subreddit', {
@ -465,6 +629,7 @@ subredditRoutes.get('/r/:subreddit/:sort?', (req, res, next) => {
error: true,
data: processed_json,
user_preferences: req.cookies,
instance_config: config,
});
}
}
@ -492,6 +657,7 @@ subredditRoutes.get('/r/:subreddit/:sort?', (req, res, next) => {
return res.render('subreddit', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
} else {
console.log(
@ -506,7 +672,8 @@ subredditRoutes.get('/r/:subreddit/:sort?', (req, res, next) => {
'from_online',
api_type,
api_target,
subreddit
subreddit,
api_mode
);
} else {
let processed_json = await processJsonSubreddit(
@ -533,6 +700,7 @@ subredditRoutes.get('/r/:subreddit/:sort?', (req, res, next) => {
redis_key: key,
after: req.query.after,
before: req.query.before,
instance_config: config,
});
}
})();
@ -549,10 +717,11 @@ subredditRoutes.get('/r/:subreddit/:sort?', (req, res, next) => {
);
console.error(config.reddit_api_error_text);
}
return res.render('index', {
return res.render('frontpage', {
json: null,
http_status_code: result.status,
user_preferences: req.cookies,
instance_config: config,
});
}
})
@ -572,17 +741,30 @@ subredditRoutes.get(
let subreddit = req.params.subreddit;
let id = req.params.id;
let snippet = encodeURIComponent(req.params.snippet);
let sortby = req.query.sort;
let sortby = req.query.sort || req.cookies.default_comment_sort;
let comment_id = '';
let viewing_comment = false;
let comment_ids = req.query.comment_ids;
let context = parseInt(req.query.context);
let api_req = req.query.api;
let api_type = req.query.type;
let api_target = req.query.target;
if (req.query.hasOwnProperty('api')) api_req = true;
else api_req = false;
let raw_json = api_req && req.query.raw_json == '1' ? 1 : 0;
if (req.params.comment_id) {
comment_id = `${req.params.comment_id}/`;
viewing_comment = true;
}
if (sortby === 'best') {
// in Reddit the sorting "best" is the label, but the actual key "confidence"
sortby = 'confidence';
}
if (!sortby) {
sortby = config.post_comments_sort;
}
@ -604,7 +786,7 @@ subredditRoutes.get(
let comments_url = `/r/${subreddit}/comments/${id}/${snippet}/${comment_id}`;
let post_url = `/r/${subreddit}/comments/${id}/${snippet}/`;
let comments_key = `${comments_url}:sort:${sortby}`;
let comments_key = `${comments_url}:sort:${sortby}:raw_json:${raw_json}`;
redis.get(comments_key, (error, json) => {
if (error) {
@ -612,64 +794,77 @@ subredditRoutes.get(
`Error getting the ${comments_url} key from redis.`,
error
);
return res.render('index', {
return res.render('frontpage', {
post: null,
user_preferences: req.cookies,
instance_config: config,
});
}
if (json) {
console.log(`Got ${comments_url} key from redis.`);
(async () => {
let parsed = false;
let more_comments = null;
if (comment_ids) {
let key = `${post_url}:morechildren:comment_ids:${comment_ids}`;
more_comments = await moreComments(
fetch,
redis,
post_url,
comment_ids,
id
if (api_req) {
return handleTedditApiPost(
json,
req,
res,
'redis',
api_type,
api_target
);
} else {
let parsed = false;
let more_comments = null;
if (comment_ids) {
let key = `${post_url}:morechildren:comment_ids:${comment_ids}`;
more_comments = await processMoreComments(
fetch,
redis,
post_url,
comment_ids,
id
);
if (more_comments === false) {
return res.redirect(post_url);
} else {
json = JSON.parse(json);
json[1].data.children = more_comments;
parsed = true;
if (more_comments === false) {
return res.redirect(post_url);
} else {
json = JSON.parse(json);
json[1].data.children = more_comments;
parsed = true;
}
}
}
let processed_json = await processJsonPost(json, parsed, req.cookies);
let finalized_json = await finalizeJsonPost(
processed_json,
id,
post_url,
more_comments,
viewing_comment,
req.cookies
);
return res.render('post', {
post: finalized_json.post_data,
comments: finalized_json.comments,
viewing_comment: viewing_comment,
post_url: post_url,
subreddit: subreddit,
sortby: sortby,
user_preferences: req.cookies,
instance_nsfw_enabled: config.nsfw_enabled,
instance_videos_muted: config.videos_muted,
post_media_max_heights: config.post_media_max_heights,
redis_key: comments_key,
});
let processed_json = await processJsonPost(json, parsed, req.cookies);
let finalized_json = await finalizeJsonPost(
processed_json,
id,
post_url,
more_comments,
viewing_comment,
req.cookies
);
return res.render('post', {
post: finalized_json.post_data,
comments: finalized_json.comments,
viewing_comment: viewing_comment,
post_url: post_url,
subreddit: subreddit,
sortby: sortby,
user_preferences: req.cookies,
instance_nsfw_enabled: config.nsfw_enabled,
instance_videos_muted: config.videos_muted,
post_media_max_heights: config.post_media_max_heights,
redis_key: comments_key,
instance_config: config,
});
}
})();
} else {
let url = '';
if (config.use_reddit_oauth)
url = `https://oauth.reddit.com${comments_url}?api_type=json&sort=${sortby}&context=${context}`;
url = `https://oauth.reddit.com${comments_url}?api_type=json&sort=${sortby}&context=${context}&raw_json=${raw_json}`;
else
url = `https://reddit.com${comments_url}.json?api_type=json&sort=${sortby}&context=${context}`;
url = `https://reddit.com${comments_url}.json?api_type=json&sort=${sortby}&context=${context}&raw_json=${raw_json}`;
fetch(encodeURI(url), redditApiGETHeaders())
.then((result) => {
@ -688,55 +883,69 @@ subredditRoutes.get(
return res.render('post', {
post: null,
user_preferences: req.cookies,
instance_config: config,
});
} else {
console.log(
`Fetched the JSON from reddit.com${comments_url}.`
);
(async () => {
let more_comments = null;
if (comment_ids) {
let key = `${post_url}:morechildren:comment_ids:${comment_ids}`;
more_comments = await moreComments(
fetch,
redis,
post_url,
comment_ids,
id
if (api_req) {
return handleTedditApiPost(
json,
req,
res,
'from_online',
api_type,
api_target
);
} else {
let more_comments = null;
if (comment_ids) {
let key = `${post_url}:morechildren:comment_ids:${comment_ids}`;
more_comments = await processMoreComments(
fetch,
redis,
post_url,
comment_ids,
id
);
if (more_comments === false) {
return res.redirect(post_url);
} else {
json[1].data.children = more_comments;
if (more_comments === false) {
return res.redirect(post_url);
} else {
json[1].data.children = more_comments;
}
}
}
let processed_json = await processJsonPost(
json,
true,
req.cookies
);
let finalized_json = await finalizeJsonPost(
processed_json,
id,
post_url,
more_comments,
viewing_comment
);
return res.render('post', {
post: finalized_json.post_data,
comments: finalized_json.comments,
viewing_comment: viewing_comment,
post_url: post_url,
subreddit: subreddit,
sortby: sortby,
user_preferences: req.cookies,
instance_nsfw_enabled: config.nsfw_enabled,
instance_videos_muted: config.videos_muted,
post_media_max_heights: config.post_media_max_heights,
redis_key: comments_key,
});
let processed_json = await processJsonPost(
json,
true,
req.cookies
);
let finalized_json = await finalizeJsonPost(
processed_json,
id,
post_url,
more_comments,
viewing_comment,
req.cookies
);
return res.render('post', {
post: finalized_json.post_data,
comments: finalized_json.comments,
viewing_comment: viewing_comment,
post_url: post_url,
subreddit: subreddit,
sortby: sortby,
user_preferences: req.cookies,
instance_nsfw_enabled: config.nsfw_enabled,
instance_videos_muted: config.videos_muted,
post_media_max_heights: config.post_media_max_heights,
redis_key: comments_key,
instance_config: config,
});
}
})();
}
}
@ -751,11 +960,12 @@ subredditRoutes.get(
);
console.error(config.reddit_api_error_text);
}
return res.render('index', {
return res.render('frontpage', {
json: null,
http_status_code: result.status,
http_statustext: result.statusText,
user_preferences: req.cookies,
instance_config: config,
});
}
})
@ -800,6 +1010,14 @@ subredditRoutes.get('/subreddits/:sort?', (req, res, next) => {
let before = req.query.before;
let sortby = req.params.sort;
let searching = false;
let api_req = req.query.api;
let api_type = req.query.type;
let api_target = req.query.target;
if (req.query.hasOwnProperty('api')) api_req = true;
else api_req = false;
let raw_json = api_req && req.query.raw_json == '1' ? 1 : 0;
if (!after) {
after = '';
@ -821,66 +1039,81 @@ subredditRoutes.get('/subreddits/:sort?', (req, res, next) => {
sortby = '';
}
let key = `subreddits:sort:${sortby}${d}`;
let key = `subreddits:sort:${sortby}${d}:raw_json:${raw_json}`;
if (sortby === 'search') {
if (typeof q == 'undefined' || q == '') return res.redirect('/subreddits');
key = `subreddits:search:q:${q}:nsfw:${nsfw}${d}`;
key = `subreddits:search:q:${q}:nsfw:${nsfw}${d}:raw_json:${raw_json}`;
searching = true;
}
redis.get(key, (error, json) => {
if (error) {
console.error(`Error getting the subreddits key from redis.`, error);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
}
if (json) {
console.log(`Got subreddits key from redis.`);
(async () => {
let processed_json = await processJsonSubredditsExplore(
json,
'redis',
null,
req.cookies
);
if (!processed_json.error) {
return res.render('subreddits_explore', {
json: processed_json,
sortby: sortby,
after: after,
before: before,
q: q,
nsfw: nsfw,
searching: searching,
subreddits_front: !before && !after ? true : false,
user_preferences: req.cookies,
instance_nsfw_enabled: config.nsfw_enabled,
});
if (api_req) {
return handleTedditApiSubredditsExplore(
json,
req,
res,
'redis',
api_type,
api_target,
q
);
} else {
return res.render('subreddits_explore', {
json: null,
error: true,
data: processed_json,
user_preferences: req.cookies,
});
let processed_json = await processJsonSubredditsExplore(
json,
'redis',
null,
req.cookies
);
if (!processed_json.error) {
return res.render('subreddits_explore', {
json: processed_json,
sortby: sortby,
after: after,
before: before,
q: q,
nsfw: nsfw,
searching: searching,
subreddits_front: !before && !after ? true : false,
user_preferences: req.cookies,
instance_nsfw_enabled: config.nsfw_enabled,
instance_config: config,
});
} else {
return res.render('subreddits_explore', {
json: null,
error: true,
data: processed_json,
user_preferences: req.cookies,
instance_config: config,
});
}
}
})();
} else {
let url = '';
if (config.use_reddit_oauth) {
if (!searching)
url = `https://oauth.reddit.com/subreddits/${sortby}?api_type=json&count=25&g=GLOBAL&t=${d}`;
url = `https://oauth.reddit.com/subreddits/${sortby}?api_type=json&count=25&g=GLOBAL&t=${d}&raw_json=${raw_json}`;
else
url = `https://oauth.reddit.com/subreddits/search?api_type=json&q=${q}&include_over_18=${nsfw}${d}`;
url = `https://oauth.reddit.com/subreddits/search?api_type=json&q=${q}&include_over_18=${nsfw}${d}&raw_json=${raw_json}`;
} else {
if (!searching)
url = `https://reddit.com/subreddits/${sortby}.json?api_type=json&count=25&g=GLOBAL&t=${d}`;
url = `https://reddit.com/subreddits/${sortby}.json?api_type=json&count=25&g=GLOBAL&t=${d}&raw_json=${raw_json}`;
else
url = `https://reddit.com/subreddits/search.json?api_type=json&q=${q}&include_over_18=${nsfw}${d}`;
url = `https://reddit.com/subreddits/search.json?api_type=json&q=${q}&include_over_18=${nsfw}${d}&raw_json=${raw_json}`;
}
fetch(encodeURI(url), redditApiGETHeaders())
@ -899,28 +1132,42 @@ subredditRoutes.get('/subreddits/:sort?', (req, res, next) => {
return res.render('subreddits_explore', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
} else {
console.log(`Fetched the JSON from reddit.com/subreddits.`);
(async () => {
let processed_json = await processJsonSubredditsExplore(
json,
'from_online',
null,
req.cookies
);
return res.render('subreddits_explore', {
json: processed_json,
sortby: sortby,
after: after,
before: before,
q: q,
nsfw: nsfw,
searching: searching,
subreddits_front: !before && !after ? true : false,
user_preferences: req.cookies,
instance_nsfw_enabled: config.nsfw_enabled,
});
if (api_req) {
return handleTedditApiSubredditsExplore(
json,
req,
res,
'from_online',
api_type,
api_target,
q
);
} else {
let processed_json = await processJsonSubredditsExplore(
json,
'from_online',
null,
req.cookies
);
return res.render('subreddits_explore', {
json: processed_json,
sortby: sortby,
after: after,
before: before,
q: q,
nsfw: nsfw,
searching: searching,
subreddits_front: !before && !after ? true : false,
user_preferences: req.cookies,
instance_nsfw_enabled: config.nsfw_enabled,
instance_config: config,
});
}
})();
}
});
@ -934,10 +1181,11 @@ subredditRoutes.get('/subreddits/:sort?', (req, res, next) => {
);
console.error(config.reddit_api_error_text);
}
return res.render('index', {
return res.render('frontpage', {
json: null,
http_status_code: result.status,
user_preferences: req.cookies,
instance_config: config,
});
}
})

View File

@ -2,16 +2,11 @@ const config = require('../config');
const { redis, fetch } = require('../app');
const userRoutes = require('express').Router();
const processUser = require('../inc/processJsonUser.js')();
const processPost = require('../inc/processJsonPost.js')();
const processAbout = require('../inc/processSubredditAbout.js')();
const processJsonUser = require('../inc/processJsonUser.js');
const tedditApiUser = require('../inc/teddit_api/handleUser.js')();
const processSearches = require('../inc/processSearchResults.js')();
const processSubreddit = require('../inc/processJsonSubreddit.js')();
const processJsonSubreddit = require('../inc/processJsonSubreddit.js');
const tedditApiSubreddit = require('../inc/teddit_api/handleSubreddit.js')();
const processMoreComments = require('../inc/processMoreComments.js')();
const processSubredditsExplore =
require('../inc/processSubredditsExplore.js')();
userRoutes.get('/user/:user/:kind?', (req, res, next) => {
let kind = '';
@ -96,9 +91,10 @@ userRoutes.get('/u/:user/:kind?', (req, res, next) => {
redis.get(key, (error, json) => {
if (error) {
console.error(`Error getting the user ${key} key from redis.`, error);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
}
if (json) {
@ -131,6 +127,7 @@ userRoutes.get('/u/:user/:kind?', (req, res, next) => {
sortby: sortby,
past: past,
user_preferences: req.cookies,
instance_config: config,
});
}
})();
@ -168,9 +165,10 @@ userRoutes.get('/u/:user/:kind?', (req, res, next) => {
`Error setting the user ${key} key to redis.`,
error
);
return res.render('index', {
return res.render('frontpage', {
post: null,
user_preferences: req.cookies,
instance_config: config,
});
} else {
(async () => {
@ -201,22 +199,33 @@ userRoutes.get('/u/:user/:kind?', (req, res, next) => {
sortby: sortby,
past: past,
user_preferences: req.cookies,
instance_config: config,
});
}
})();
}
}
);
}).catch(error => {
console.error(`Something went wrong while fetching data from Reddit API: invalid or non-JSON data was returned.`);
return res.render('frontpage', {
json: null,
http_status_code: 500,
http_statustext: "Invalid response from Reddit",
user_preferences: req.cookies,
instance_config: config,
});
});
} else {
console.error(
`Something went wrong while fetching data from Reddit. ${result.status} ${result.statusText}`
);
console.error(config.reddit_api_error_text);
return res.render('index', {
return res.render('frontpage', {
json: null,
http_status_code: result.status,
user_preferences: req.cookies,
instance_config: config,
});
}
})
@ -225,10 +234,11 @@ userRoutes.get('/u/:user/:kind?', (req, res, next) => {
`Error fetching the overview JSON file from reddit.com/u/${user}`,
error
);
return res.render('index', {
return res.render('frontpage', {
json: null,
http_status_code: result.status,
user_preferences: req.cookies,
instance_config: config,
});
});
});
@ -241,11 +251,12 @@ userRoutes.get('/u/:user/:kind?', (req, res, next) => {
);
console.error(config.reddit_api_error_text);
}
return res.render('index', {
return res.render('frontpage', {
json: null,
http_status_code: result.status,
http_statustext: result.statusText,
user_preferences: req.cookies,
instance_config: config,
});
}
})
@ -316,9 +327,10 @@ userRoutes.get('/u/:user/m/:custom_feed/:sort?', (req, res, next) => {
`Error getting the ${user} custom_feed key from redis.`,
error
);
return res.render('index', {
return res.render('frontpage', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
}
if (json) {
@ -332,7 +344,8 @@ userRoutes.get('/u/:user/m/:custom_feed/:sort?', (req, res, next) => {
'redis',
api_type,
api_target,
subreddit
subreddit,
'full'
);
} else {
let processed_json = await processJsonSubreddit(
@ -354,6 +367,7 @@ userRoutes.get('/u/:user/m/:custom_feed/:sort?', (req, res, next) => {
redis_key: key,
after: req.query.after,
before: req.query.before,
instance_config: config,
});
} else {
return res.render('subreddit', {
@ -361,6 +375,7 @@ userRoutes.get('/u/:user/m/:custom_feed/:sort?', (req, res, next) => {
error: true,
data: processed_json,
user_preferences: req.cookies,
instance_config: config,
});
}
}
@ -388,6 +403,7 @@ userRoutes.get('/u/:user/m/:custom_feed/:sort?', (req, res, next) => {
return res.render('subreddit', {
json: null,
user_preferences: req.cookies,
instance_config: config,
});
} else {
console.log(
@ -402,7 +418,8 @@ userRoutes.get('/u/:user/m/:custom_feed/:sort?', (req, res, next) => {
'from_online',
api_type,
api_target,
subreddit
subreddit,
'full'
);
} else {
let processed_json = await processJsonSubreddit(
@ -423,6 +440,7 @@ userRoutes.get('/u/:user/m/:custom_feed/:sort?', (req, res, next) => {
redis_key: key,
after: req.query.after,
before: req.query.before,
instance_config: config,
});
}
})();
@ -439,10 +457,11 @@ userRoutes.get('/u/:user/m/:custom_feed/:sort?', (req, res, next) => {
);
console.error(config.reddit_api_error_text);
}
return res.render('index', {
return res.render('frontpage', {
json: null,
http_status_code: result.status,
user_preferences: req.cookies,
instance_config: config,
});
}
})

211
static/css/nord.css Normal file
View File

@ -0,0 +1,211 @@
:root {
--darkbg: #2E3440;
--darkbglight: #434C5E;
--darklinkcolor: #599bff;
}
body.nord {
background: var(--darkbg);
color: #ECEFF4;
}
body.nord nav {
background: #3E4450;
}
body.nord .top-links a {
background: var(--darkbg);
color: #bfbfbf;
}
body.nord header {
background: var(--darkbglight);
color: #f1f1f1;
}
body.nord #post header div a {
color: var(--darklinkcolor);
text-decoration: none;
}
body.nord a {
color: #f5f5f5;
}
body.nord a:hover, body.nord a:focus {
color: #88C0D0;
text-decoration: underline;
}
body.nord #post header div a:hover,
body.nord #post header div a:focus {
text-decoration: underline;
}
body.nord input[type="submit"]:hover,
body.nord input[type="submit"]:focus,
body.nord .btn:hover,
body.nord .btn:focus {
background: #ECEFF4;
color: #2E3440;
text-decoration: none;
}
body.nord form legend {
border-bottom: 1px solid #353535;
}
body.nord #post .title a {
color: var(--darklinkcolor);
}
body.nord #post .submitted {
color: #a5a5a5;
}
body.nord #post .usertext-body {
background: #2A2935;
border: 1px solid #404040;
}
body.nord .infobar {
background-color: #d2d2d2;
color: #2f2f2f;
}
body.nord .infobar.blue {
background: #c7e3f9;
border: 1px solid #4b78a4;
}
body.nord .infobar a {
color: #0356d4;
}
body.nord header .tabmenu li a {
background: #3e3e3e;
}
body.nord header .tabmenu li a:hover, body.nord header .tabmenu li a:focus {
text-decoration: underline;
color: #ECEFF4;
}
body.nord #search {
color: #d2d2d2;
}
body.nord .md {
color: #dadada;
}
body.nord .md blockquote, body.nord .md del {
color: #777777;
}
body.nord .md code, body.nord .md pre {
background: #2E3440;
color: #cacaca;
}
body.nord .comment .body blockquote {
background: #2E3440;
color: #afafaf;
border-color: #464646;
}
body.nord .even-depth {
background: var(--darkbg);
}
body.nord .odd-depth {
background: var(--darkbglight);
}
body.nord .comment .comment {
border-left: 1px solid #545454;
}
body.nord .comment .meta .created a {
color: #7b7b7b;
}
body.nord .comment details summary {
color: #868686;
}
body.nord .comment details summary::-webkit-details-marker,
body.nord .comment details summary::marker {
color: #868686;
}
body.nord #links .link .entry .title a h2 {
color: #f0f0f0;
}
body.nord #links .link .entry .title a:visited h2 {
color: #8FBCBB;
}
body.nord #links .link .image .no-image,
body.nord #user .entry .image .no-image {
filter: opacity(0.5);
}
body.nord #user .comment {
width: 100%;
background: var(--darkbg);
}
body.nord #links .link .upvotes {
color: #D08770;
}
body.nord .upvotes .arrow,
body.nord .score .arrow {
filter: opacity(0.5);
}
body.nord #links .link .entry .meta a {
color: #81A1C1;
}
#links .link .entry .meta {
color: #D8DEE9 !important;
}
#links .link .entry .title span {
color: #5E81AC !important;
}
body.nord #links .link .entry .selftext {
background: #2A2935;
border: 1px solid #404040;
}
body.nord #links .link .entry .meta .links .selftext a {
color: var(--darklinkcolor);
margin: 0;
}
body.nord #links .link .entry details .line {
width: 16px;
margin-top: 3px;
background: #4C566A;
border: 1px solid #81A1C1;
}
body.nord .content .bottom img {
filter: invert(1);
}
body.nord .container .content {
border: 1px solid #434C5E;
}
body.nord input[type="submit"],
body.nord .btn {
background: #2E3440;
color: #ECEFF4;
}
body.nord #post .crosspost {
background: var(--darkbg);
}
body.nord .view-more-links a {
background: #2E3440;
color: #cacaca;
}
body.nord .md .md-spoiler-text:not(.revealed):active,
body.nord .md .md-spoiler-text:not(.revealed):focus,
body.nord .md .md-spoiler-text:not(.revealed):hover {
background: #cacaca;
color: #2E3440;
}
body.nord .comment .body a,
body.nord .usertext-body a {
color: #3d99fb;
}
body.nord header .tabmenu li.active a {
background: #acacac;
color: #151515;
}
body.nord #search form input[type="text"] {
background: #2E3440;
color: #ECEFF4;
}
body.nord footer {
background: #2f2f2f;
}
body.nord footer a {
color: #999;
}
body.nord .flair {
color: #eaeaea !important;
background-color: #404040 !important;
}
body.nord #sr-more-link {
color: #ECEFF4;
background: #2E3440;
}
body.nord #post .usertext-body .poll {
border: 1px solid #404040;
}

View File

@ -84,6 +84,7 @@ nav .nav-item.left a {
}
nav .nav-item.left img {
width: 20px;
height: 20px;
vertical-align: bottom;
margin: 0 7px 0 0;
}
@ -280,9 +281,11 @@ header .tabmenu li.active a {
color: white;
padding: 16px;
}
input, select {
padding: 2px;
}
input[type="submit"],
.btn {
padding: 3px;
margin-top: 7px;
margin-right: 10px;
border-radius: 0;
@ -749,6 +752,14 @@ footer a {
#post .submitted span {
margin-left: 5px;
}
#post .source-details {
float: left;
margin: 10px 0 10px 30px;
}
#post .source-details summary:hover {
color: var(--linkcolor);
text-decoration: underline;
}
#post .comments {
float: left;
width: 100%;
@ -1572,6 +1583,103 @@ code {
padding:4px;
margin:5px 0
}
/* "CLEANED HOMEPAGE" SECTION */
body.homepage.clean {
margin: 0;
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
body.homepage.clean main {
flex-grow: 1;
display: flex;
width: 100%;
flex-direction: column;
justify-content: center;
align-items: center;
}
body.homepage.clean h1 {
margin-bottom: 1rem;
font-size: 3rem;
text-align: center;
width: 100%;
}
body.homepage.clean form {
width: 100vw;
max-width: 750px;
text-align: center;
}
body.homepage.clean input[name="q"] {
width: 90%;
padding: 0.4rem;
border: none;
color: white;
background: #555;
margin-bottom: 1rem;
}
body.homepage.clean .sublinks {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
max-width: 650px;
}
body.homepage.clean .sublinks a {
color: gray;
margin-right: 0.3rem;
}
.homepage.clean .top-links {
display: none;
}
@media only screen and (max-width: 768px) {
body.homepage.clean form, body.homepage.clean .sublinks {
width: 90%;
max-width: unset;
}
}
/* Large gallery items */
.gallery .item.large {
display: flex;
flex-direction: column;
margin-bottom: 1rem;
position: relative;
margin-right: 0.3rem;
}
.gallery .item.large img {
max-height: 90vh;
position: relative;
}
.gallery .item.large .caption {
position: absolute;
width: calc(100% - 0.6rem);
color: white;
background: rgba(0, 0, 0, 0.7);
padding: 0.3rem;
bottom: 0;
}
@media only screen and (max-width: 768px) {
.gallery .item.large img {
max-height: unset;
max-width: 100%;
}
}
/* Fix spoiler texts not showing without JS */
.md .md-spoiler-text:not(.revealed):active,.md .md-spoiler-text:not(.revealed):focus,.md .md-spoiler-text:not(.revealed):hover {
color: black;

1
static/instances.json Symbolic link
View File

@ -0,0 +1 @@
../instances.json

BIN
static/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -1,2 +1,3 @@
User-agent: MJ12bot
User-agent: Applebot
Disallow: /

View File

@ -2,6 +2,9 @@ doctype html
html
head
title about - teddit
meta(property='og:title', content='about - teddit')
include includes/meta_default.pug
include includes/meta_description.pug
include includes/head.pug
body(class=""+ (user_preferences.theme === 'auto' ? 'dark' : user_preferences.theme) + "")
include includes/topbar.pug
@ -18,8 +21,10 @@ html
a(href="/privacy") Privacy policy
h2 Donating
p(class="word-break") XMR: 832ogRwuoSs2JGYg7wJTqshidK7dErgNdfpenQ9dzMghNXQTJRby1xGbqC3gW3GAifRM9E84J91VdMZRjoSJ32nkAZnaCEj
h2 Legal
p Teddit does not host any content. All content shown on any Teddit instances is from Reddit™. Reddit is a trademark of Reddit Inc. Teddit is not affiliated with Reddit Inc. Any issues with content shown on any Teddit instances need to be reported to Reddit, not the instance host's internet provider or domain provider.
.bottom
a(href="https://en.wikipedia.org/wiki/Piratbyr%C3%A5n#Kopimi", target="_blank")
img(src="kopimi.gif")
p.version v.0.4.0
p.version v.0.4.9
include includes/footer.pug

View File

@ -1,4 +1,9 @@
.link
if user_preferences.show_upvotes === 'false'
style.
.upvotes {
display: none;
}
.upvotes
.arrow
span #{kFormatter(link.ups)}
@ -92,3 +97,4 @@
a(href="/unsave/" + link.id + "/?rk=" + redis_key + "&b=" + back_url + "") unsave
else
a(href="/save/" + link.id + "/?rk=" + redis_key + "&b=" + back_url + "") save
a(href=("https://www.reddit.com" + link.permalink) target="_blank" title="Open in Reddit (new tab)") [R↗]

View File

@ -2,6 +2,9 @@ doctype html
html
head
title teddit
meta(property='og:title', content='frontpage : teddit')
include includes/meta_default.pug
include includes/meta_description.pug
include includes/head.pug
body(class=""+ (user_preferences.theme === 'auto' ? 'dark' : user_preferences.theme) + "")
include includes/topbar.pug
@ -38,6 +41,7 @@ html
#intro
h1 Welcome to teddit
h2 the alternative, privacy respecting, front page of internet.
#links.sr
if sortby === 'top' || sortby === 'controversial'
details

33
views/homepage.pug Normal file
View File

@ -0,0 +1,33 @@
doctype html
html
head
title teddit
meta(property='og:title', content='home : teddit')
include includes/meta_default.pug
include includes/meta_description.pug
include includes/head.pug
body(class="" + (user_preferences.theme === 'auto' ? 'dark' : user_preferences.theme) + " homepage clean")
include includes/topbar.pug
main
h1 teddit
form(action="/search", method="GET")
input(type="text", name="q")
input(type="hidden", name="restrict_sr", value="on")
input(type="hidden", name="nsfw", value="on")
input(type="hidden", name="sort", value="relevance")
input(type="hidden", name="t", value="all")
.sublinks
if user_preferences.subbed_subreddits && Array.isArray(user_preferences.subbed_subreddits)
a(href="/r/popular") Popular
a(href="/r/all") All
a(href="/saved") Saved
each subreddit in user_preferences.subbed_subreddits
a(href="/r/" + subreddit) #{subreddit}
else if instance_config.suggested_subreddits && Array.isArray(instance_config.suggested_subreddits)
each subreddit in instance_config.suggested_subreddits
if subreddit.toLowerCase() === 'saved'
a(href="/saved") Saved
else
a(href="/r/" + subreddit) #{subreddit}
include includes/footer.pug

View File

@ -4,8 +4,11 @@ if(user_preferences.theme === 'dark')
link(rel="stylesheet", type="text/css", href="/css/dark.css")
if(user_preferences.theme === 'sepia')
link(rel="stylesheet", type="text/css", href="/css/sepia.css")
if(user_preferences.theme === 'nord')
link(rel="stylesheet", type="text/css", href="/css/nord.css")
link(rel="stylesheet", type="text/css", href="/css/styles.css")
link(rel="icon", type="image/png", sizes="32x32", href="/favicon.png")
meta(property='og:site_name', content='teddit')
meta(name="viewport", content="width=device-width, initial-scale=1.0")
-
if(!user_preferences)

View File

@ -0,0 +1,3 @@
meta(property='og:type', content='website')
meta(name='twitter:card', content='summary')
include meta_logo.pug

View File

@ -0,0 +1 @@
meta(property='og:description', content='Teddit is a free and open source alternative Reddit front-end focused on privacy. Teddit doesn\'t require you to have JavaScript enabled in your browser.')

View File

@ -0,0 +1,4 @@
meta(property='og:image', content='/logo512.png')
meta(property='og:image:width', content='128')
meta(property='og:image:height', content='128')
meta(property='og:author_name', content='')

View File

@ -0,0 +1,43 @@
meta(property='og:title', content=cleanTitle(post.title) + ' : r/' + subreddit)
meta(property='og:description', content='' + post.selftext_preview)
meta(property='og:author_name', content='u/' + post.author)
if !post.has_media
if post.gallery
meta(property='twitter:card', content='summary_large_image')
meta(property='og:type', content='image')
each item in post.gallery_items
meta(property='og:image', content='' + item.large)
else
if post.images
meta(property='twitter:card', content='summary_large_image')
meta(property='og:type', content='image')
meta(property='og:image', content='' + post.images.source)
meta(property='og:image:url', content='' + post.images.source)
else
include meta_logo.pug
else
if post.media
if post.media.not_hosted_in_reddit
if post.media.source === 'YouTube'
meta(property='twitter:card', content='player')
meta(property='og:type', content='video')
meta(property='og:video', content='' + post.media.embed_src)
else
if post.media.source === 'external'
if post.images
meta(name='twitter:card', content='summary_large_image')
meta(property='og:type', content='image')
meta(property='og:image', content='' + post.images.source)
meta(property='og:image:src', content='' + post.images.source)
else
meta(name='twitter:card', content='summary_large_image')
meta(property='og:type', content='image')
meta(property='og:image', content='' + post.media.source)
meta(property='og:image:url', content='' + post.media.source)
else
meta(property='twitter:card', content='player')
meta(property='og:type', content='video')
meta(property='og:video', content='' + post.media.source)
meta(property='og:video:type', content='video/mp4')
else
include meta_logo.pug

View File

@ -16,55 +16,10 @@ div#topbar
a(href="/saved") Saved
each subreddit in user_preferences.subbed_subreddits
a(href="/r/" + subreddit) #{subreddit}
else
a(href="/r/popular") Popular
a(href="/r/all") All
a(href="/saved") Saved
a(href="/r/AskReddit") AskReddit
a(href="/r/pics") pics
a(href="/r/news") news
a(href="/r/worldnews") worldnews
a(href="/r/funny") funny
a(href="/r/tifu") tifu
a(href="/r/videos") videos
a(href="/r/gaming") gaming
a(href="/r/aww") aww
a(href="/r/todayilearned") todayilearned
a(href="/r/gifs") gifs
a(href="/r/Art") Art
a(href="/r/explainlikeimfive") explainlikeimfive
a(href="/r/movies") movies
a(href="/r/Jokes") Jokes
a(href="/r/TwoXChromosomes") TwoXChromosomes
a(href="/r/mildlyinteresting") mildlyinteresting
a(href="/r/LifeProTips") LifeProTips
a(href="/r/askscience") askscience
a(href="/r/IAmA") IAmA
a(href="/r/dataisbeautiful") dataisbeautiful
a(href="/r/books") books
a(href="/r/science") science
a(href="/r/Showerthoughts") Showerthoughts
a(href="/r/gadgets") gadgets
a(href="/r/Futurology") Futurology
a(href="/r/nottheonion") nottheonion
a(href="/r/history") history
a(href="/r/sports") sports
a(href="/r/OldSchoolCool") OldSchoolCool
a(href="/r/GetMotivated") GetMotivated
a(href="/r/DIY") DIY
a(href="/r/photoshopbattles") photoshopbattles
a(href="/r/nosleep") nosleep
a(href="/r/Music") Music
a(href="/r/space") space
a(href="/r/food") food
a(href="/r/UpliftingNews") UpliftingNews
a(href="/r/EarthPorn") EarthPorn
a(href="/r/Documentaries") Documentaries
a(href="/r/InternetIsBeautiful") InternetIsBeautiful
a(href="/r/WritingPrompts") WritingPrompts
a(href="/r/creepy") creepy
a(href="/r/philosophy") philosophy
a(href="/r/announcements") announcements
a(href="/r/listentothis") listentothis
a(href="/r/blog") blog
else if instance_config.suggested_subreddits && Array.isArray(instance_config.suggested_subreddits)
each subreddit in instance_config.suggested_subreddits
if subreddit.toLowerCase() === 'saved'
a(href="/saved") Saved
else
a(href="/r/" + subreddit) #{subreddit}
a(href="/subreddits", id="sr-more-link") more »

View File

@ -2,6 +2,7 @@ doctype html
html
head
title #{cleanTitle(post.title)} : #{subreddit}
include includes/meta_post.pug
include includes/head.pug
body(class=""+ (user_preferences.theme === 'auto' ? 'dark' : user_preferences.theme) + "")
include includes/topbar.pug
@ -46,6 +47,12 @@ html
video_muted = false
}
.info
if user_preferences.show_upvotes === 'false'
style.
.comment .meta .ups, #post .score, #user .upvotes {
display: none;
}
.score
div.arrow
span #{kFormatter(post.ups)}
@ -80,6 +87,7 @@ html
a(href="/unsave/" + post.id + "/?rk=" + redis_key + "&b=" + back_url + "") unsave
else
a(href="/save/" + post.id + "/?rk=" + redis_key + "&b=" + back_url + "") save
a(href=("https://www.reddit.com" + back_url) target="_blank" title="Open in Reddit (new tab)" style="margin-left: 10px") [R↗]
if post.crosspost.is_crosspost === true
.crosspost
.title
@ -130,12 +138,19 @@ html
if post.gallery
.gallery
each item in post.gallery_items
.item
div
if user_preferences.show_large_gallery_images == 'true'
.item.large
a(href="" + item.large + "", target="_blank")
img(src=""+ item.thumbnail +"", alt="")
a(href="" + item.source + "", target="_blank", class="source-link")
small source
img(src="" + item.large + "", title="" + item.caption + "")
if item.caption
span.caption !{item.caption}
else
.item
div
a(href="" + item.large + "", target="_blank")
img(src=""+ item.thumbnail +"", alt="")
a(href="" + item.source + "", target="_blank", class="source-link")
small source
if post.images
.image
a(href="" + post.images.source + "")
@ -163,10 +178,11 @@ html
a(href="" + post.media.embed_src + "", target="_blank", rel="noopener noreferrer")
img(src="" + post.images.source + "", alt="")
if !post.media.embed_src.startsWith("https://twitter.com")
p
| source:
a(href="" + post.media.embed_src + "", target="_blank", rel="noopener noreferrer")
p(class="source-url") #{post.media.embed_src}
details(class="source-details")
summary Source
p
a(href="" + post.media.embed_src + "", target="_blank", rel="noopener noreferrer")
p(class="source-url") #{post.media.embed_src}
else
.video
a(href="" + post.media.source + "")

View File

@ -2,6 +2,9 @@ doctype html
html
head
title preferences - teddit
meta(property='og:title', content='preferences - teddit')
include includes/meta_default.pug
include includes/meta_description.pug
include includes/head.pug
body(class=""+ (user_preferences.theme === 'auto' ? 'dark' : user_preferences.theme) + "")
include includes/topbar.pug
@ -11,23 +14,29 @@ html
form(action="/saveprefs", method="POST")
legend Privacy
.setting
label(for="domain_twitter") Replace Twitter links with Nitter (blank to disable):
label(for="domain_twitter") Replace Twitter links with <a href="https://github.com/zedeus/nitter">Nitter</a> (blank to disable):
if(user_preferences.domain_twitter != '' && typeof(user_preferences.domain_twitter) != 'undefined')
input(type="text", name="domain_twitter", id="domain_twitter", value="" + user_preferences.domain_twitter + "", placeholder="e.g. nitter.net")
else
input(type="text", name="domain_twitter", id="domain_twitter", placeholder="e.g. nitter.net")
.setting
label(for="domain_youtube") Replace YouTube links with Invidious (blank to disable):
label(for="domain_youtube") Replace YouTube links with <a href="https://github.com/iv-org/invidious">Invidious</a>/<a href="https://github.com/TeamPiped/Piped">Piped</a> (blank to disable):
if(user_preferences.domain_youtube != '' && typeof(user_preferences.domain_youtube) != 'undefined')
input(type="text", name="domain_youtube", id="domain_youtube", value="" + user_preferences.domain_youtube + "", placeholder="e.g. invidious.site")
else
input(type="text", name="domain_youtube", id="domain_youtube", placeholder="e.g. invidious.site")
.setting
label(for="domain_instagram") Replace Instagram links with Bibliogram (blank to disable):
if(user_preferences.domain_instagram != '' && typeof(user_preferences.domain_instagram) != 'undefined')
input(type="text", name="domain_instagram", id="domain_instagram", value="" + user_preferences.domain_instagram + "", placeholder="e.g. bibliogram.art")
label(for="domain_quora") Replace Quora links with <a href="https://codeberg.org/zyachel/quetre">Quetre</a> (blank to disable):
if(user_preferences.domain_quora != '' && typeof(user_preferences.domain_quora) != 'undefined')
input(type="text", name="domain_quora", id="domain_quora", value="" + user_preferences.domain_quora + "", placeholder="e.g. quetre.iket.me")
else
input(type="text", name="domain_instagram", id="domain_instagram", placeholder="e.g. bibliogram.art")
input(type="text", name="domain_quora", id="domain_quora", placeholder="e.g. quetre.iket.me")
.setting
label(for="domain_imgur") Replace Imgur links with <a href="https://codeberg.org/video-prize-ranch/rimgo">Rimgo</a> (blank to disable):
if(user_preferences.domain_imgur != '' && typeof(user_preferences.domain_imgur) != 'undefined')
input(type="text", name="domain_imgur", id="domain_imgur", value="" + user_preferences.domain_imgur + "", placeholder="e.g. rimgo.bcow.xyz")
else
input(type="text", name="domain_imgur", id="domain_imgur", placeholder="e.g. rimgo.bcow.xyz")
legend Display
.setting
label(for="theme") Theme:
@ -37,21 +46,37 @@ html
option(value="white") White
option(value="dark") Dark
option(value="sepia") Sepia
if(user_preferences.theme == 'white')
option(value="nord") Nord
else if(user_preferences.theme == 'white')
option(value="auto") Auto
option(value="white", selected="selected") White
option(value="dark") Dark
option(value="sepia") Sepia
if(user_preferences.theme === 'dark')
option(value="nord") Nord
else if(user_preferences.theme === 'dark')
option(value="auto") Auto
option(value="white") White
option(value="dark", selected="selected") Dark
option(value="sepia") Sepia
if(user_preferences.theme === 'sepia')
option(value="nord") Nord
else if(user_preferences.theme === 'sepia')
option(value="auto") Auto
option(value="white") White
option(value="dark") Dark
option(value="sepia", selected="selected") Sepia
option(value="nord") Nord
else if(user_preferences.theme === 'nord')
option(value="auto") Auto
option(value="white") White
option(value="dark") Dark
option(value="sepia") Sepia
option(value="nord", selected="selected") Nord
else
option(value="auto") Auto
option(value="white") White
option(value="dark") Dark
option(value="sepia") Sepia
option(value="nord") Nord
.setting
label(for="flairs") Show flairs:
if(!user_preferences.flairs || user_preferences.flairs == 'true')
@ -96,6 +121,29 @@ html
input(type="checkbox", name="show_upvoted_percentage", id="show_upvoted_percentage", checked="checked")
else
input(type="checkbox", name="show_upvoted_percentage", id="show_upvoted_percentage")
.setting
label(for="show_upvotes") Show upvotes:
if(!user_preferences.show_upvotes || user_preferences.show_upvotes == 'true')
input(type="checkbox", name="show_upvotes", id="show_upvotes", checked="checked")
else
input(type="checkbox", name="show_upvotes", id="show_upvotes")
.setting
label(for="prefer_frontpage") Prefer reddit-style frontpage as homepage:
if ((!instance_config.clean_homepage && !user_preferences.prefer_frontpage) || user_preferences.prefer_frontpage == 'true')
input(type="checkbox", name="prefer_frontpage", id="prefer_frontpage", checked="checked")
else
input(type="checkbox", name="prefer_frontpage", id="prefer_frontpage")
.setting
label(for="default_comment_sort") Default comment sorting:
select(id="default_comment_sort", name="default_comment_sort")
-
let default_comment_sort_html = ''
let user_default_sort = user_preferences.default_comment_sort || 'best'
for(let key of comment_sort_values) {
default_comment_sort_html += `<option value="${key}" ${(user_default_sort == key ? "selected" : "")}>${key == 'qa' ? 'Q&A' : key}</option>`
}
!= default_comment_sort_html
legend Media
.setting
label(for="videos_muted") Mute videos by default:
@ -111,6 +159,12 @@ html
input(type="checkbox", name="videos_muted", id="videos_muted", checked="checked")
else
input(type="checkbox", name="videos_muted", id="videos_muted")
.setting
label(for="show_large_gallery_images") Show large gallery images with captions:
if (user_preferences.show_large_gallery_images == 'true')
input(type="checkbox", name="show_large_gallery_images", id="show_large_gallery_images", checked="checked")
else
input(type="checkbox", name="show_large_gallery_images", id="show_large_gallery_images")
small(class="notice") Preferences are stored client-side using cookies without any personal information.
br
input(type="submit", value="Save preferences")

View File

@ -2,6 +2,9 @@ doctype html
html
head
title privacy policy - teddit
meta(property='og:title', content='privacy policy - teddit')
include includes/meta_default.pug
include includes/meta_description.pug
include includes/head.pug
body(class=""+ (user_preferences.theme === 'auto' ? 'dark' : user_preferences.theme) + "")
include includes/topbar.pug

View File

@ -2,6 +2,9 @@ doctype html
html
head
title saved
meta(property='og:title', content='saved - teddit')
include includes/meta_default.pug
include includes/meta_description.pug
include includes/head.pug
body(class=""+ (user_preferences.theme === 'auto' ? 'dark' : user_preferences.theme) + "")
include includes/topbar.pug

View File

@ -3,8 +3,12 @@ html
head
if no_query
title search teddit
meta(property='og:title', content='search - teddit')
else
title search results for #{q}
meta(property='og:title', content='search results for ' + q + ' - teddit')
include includes/meta_default.pug
include includes/meta_description.pug
include includes/head.pug
body(class=""+ (user_preferences.theme === 'auto' ? 'dark' : user_preferences.theme) + "")
include includes/topbar.pug
@ -140,8 +144,8 @@ html
include components/link.pug
if json.before || json.after
.view-more-links
if json.before && !subreddit_front
a(href="?q=" + q + "&restrict_sr=" + restrict_sr + "&nsfw=" + nsfw + "&before=" + json.before + "") prev
if json.before
a(href="?q=" + q + "&restrict_sr=" + restrict_sr + "&nsfw=" + nsfw + "&before=" + json.before + "" + (sortby === "new" && sortby ? "&" : "&sort=" + sortby + "&") + (!past ? "" : "t=" + past)) prev
if json.after
a(href="?q=" + q + "&restrict_sr=" + restrict_sr + "&nsfw=" + nsfw + "&after=" + json.after + "") next
a(href="?q=" + q + "&restrict_sr=" + restrict_sr + "&nsfw=" + nsfw + "&after=" + json.after + "" + (sortby === "new" && sortby ? "&" : "&sort=" + sortby + "&") + (!past ? "" : "t=" + past)) next
include includes/footer.pug

View File

@ -2,6 +2,10 @@ doctype html
html
head
title /r/#{subreddit}
meta(property='og:title', content='/r/' + subreddit)
if subreddit_about
meta(property='og:description', content='' + unescape(subreddit_about.public_description_html, user_preferences))
include includes/meta_default.pug
include includes/head.pug
body(class=""+ (user_preferences.theme === 'auto' ? 'dark' : user_preferences.theme) + "")
include includes/topbar.pug

View File

@ -2,6 +2,8 @@ doctype html
html
head
title wiki /r/#{subreddit}
meta(property='og:title', content='wiki /r/' + subreddit)
include includes/meta_default.pug
include includes/head.pug
body(class=""+ (user_preferences.theme === 'auto' ? 'dark' : user_preferences.theme) + "")
include includes/topbar.pug

View File

@ -2,6 +2,9 @@ doctype html
html
head
title subreddits - explore
meta(property='og:title', content='explore subreddits - teddit')
include includes/meta_default.pug
include includes/meta_description.pug
include includes/head.pug
body(class=""+ (user_preferences.theme === 'auto' ? 'dark' : user_preferences.theme) + "")
include includes/topbar.pug

View File

@ -2,6 +2,8 @@ doctype html
html
head
title overview for #{data.username}
meta(property='og:title', content='u/' + data.username + ' - teddit')
include includes/meta_default.pug
include includes/head.pug
body(class=""+ (user_preferences.theme === 'auto' ? 'dark' : user_preferences.theme) + "")
include includes/topbar.pug