Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
194 commits
Select commit Hold shift + click to select a range
e5412eb
chore(deps): bump pino-pretty from 13.1.2 to 13.1.3
dependabot[bot] Dec 30, 2025
69ff7af
feat: port discovery middleware and endpoints to typescript
mrjvs Jan 4, 2026
616f211
feat: port detectVersion middleware to typescript
mrjvs Jan 4, 2026
9277f39
chore: unify endpoint model for miiverse-api and juxtaposition-ui
mrjvs Jan 4, 2026
49a1e90
feat: port reports to typescript
mrjvs Jan 4, 2026
55d476c
feat: port settings model to typescript
mrjvs Jan 4, 2026
130d09a
feat: port web.js router to typescript
mrjvs Jan 4, 2026
1464671
feat: port show.js to typescript
mrjvs Jan 4, 2026
2c89b3b
feat: port first_run.ejs to jsx
mrjvs Jan 7, 2026
38d4b7a
feat: implement first_run jsx view
mrjvs Jan 7, 2026
ee56fd2
feat: port web routers to ts
mrjvs Jan 7, 2026
92e7217
feat: port login page to JSX
mrjvs Jan 7, 2026
9e08a91
feat: port core server files to typescript
mrjvs Jan 7, 2026
de49f3f
feat: port main router to ts
mrjvs Jan 7, 2026
f1f761e
fix: update build script with new entrypoint
mrjvs Jan 7, 2026
9eeca7b
fix: remove unused broken type
mrjvs Jan 7, 2026
b1b3765
Merge branch 'dev' into feat/even-more-jsx
mrjvs Jan 7, 2026
a4b3e26
fix: update toast showcase inline with new commits
mrjvs Jan 7, 2026
2f9e654
chore: remove login.ejs, unused
mrjvs Jan 7, 2026
8720984
fix: make tabs on web be next to each other
mrjvs Jan 7, 2026
a29e155
feat: add spritesheet script
ashquarky Jan 9, 2026
40da66e
fix: replace sprites with their actual rendered sizes
ashquarky Jan 9, 2026
2aa2a84
feat: make tsx feeling selector use new sprites
ashquarky Jan 9, 2026
13b552b
feat: add other sprites to new spritesheet
ashquarky Jan 10, 2026
a6d30d8
feat: port tsx feeling view to new sprites
ashquarky Jan 10, 2026
eb2a1dc
feat: port jsx post view to new sprites
ashquarky Jan 10, 2026
6d1b9db
feat: new sprites for older EJS routes too
ashquarky Jan 10, 2026
3b1d1be
feat: new sprites for community/user page
ashquarky Jan 10, 2026
3ac7d2e
Merge branch 'dev' into work/sprites
ashquarky Jan 10, 2026
d34514b
locales(update): Updated Basque locale
Jan 10, 2026
f8367c8
locales(update): Updated Basque locale
Jan 10, 2026
5828f6d
fix: tidy up old assets
ashquarky Jan 12, 2026
e522054
fix: pngcrush spritesheet
ashquarky Jan 12, 2026
d182b89
feat: use esbuild data-url support for spritesheet, rename to .sprite…
ashquarky Jan 12, 2026
528c1f1
fix(ctr): use imports for base64 URLs instead of hardcoding
ashquarky Jan 12, 2026
a8248c6
chore: gitignore macos
ashquarky Jan 13, 2026
7ebd1f7
feat(build): add oxipng plugin
ashquarky Jan 13, 2026
136d7e2
feat(build): add spritesmith plugin
ashquarky Jan 13, 2026
6083a0a
chore: lint
ashquarky Jan 13, 2026
1334a83
fix: fix login page styling
mrjvs Jan 14, 2026
717c748
fix: fix invalid login toast from showing
mrjvs Jan 14, 2026
a709725
fix: add proper doctype by default
mrjvs Jan 14, 2026
ed9733c
chore: remove impossible conditional
mrjvs Jan 14, 2026
ecf37c8
chore: optimize mii saving on show router
mrjvs Jan 14, 2026
0b9f69a
fix: check for access token existence before trying it out
mrjvs Jan 14, 2026
878eb13
fix: show only visible AboutSections on firstRunView.tsx
mrjvs Jan 14, 2026
f81b62f
fix: show visible aboutSection on ctr
mrjvs Jan 14, 2026
709024b
Merge branch 'dev' into feat/even-more-jsx
mrjvs Jan 14, 2026
a13ed6a
fix: fixed ctr onclicks in firstrun not working
mrjvs Jan 14, 2026
97e8ac3
fix: remove useless ip route
ashquarky Jan 16, 2026
bd0be5a
Merge pull request #291 from PretendoNetwork/fix/no-ip
ashquarky Jan 16, 2026
be06a2d
chore: remove leftover console logs
mrjvs Jan 16, 2026
8ba6a62
chore: replace raw SVG on JSX web with components
mrjvs Jan 16, 2026
c973549
fix: revert entrypoint changes for ctr bundling + add firsturn
mrjvs Jan 16, 2026
70e6700
Merge branch 'dev' into feat/even-more-jsx
mrjvs Jan 16, 2026
da9edac
Merge pull request #284 from PretendoNetwork/feat/even-more-jsx
mrjvs Jan 16, 2026
db69d27
Merge branch 'dev' into work/sprites
ashquarky Jan 17, 2026
116f4f7
feat: port admin router to ts
mrjvs Jan 17, 2026
86ec628
fix: fixed report model types
mrjvs Jan 17, 2026
ba27751
feat: add support for file parsing to the request utils
mrjvs Jan 17, 2026
da65a0b
feat: fix createLogEntry defaults
mrjvs Jan 17, 2026
d3323f7
fix: Add proper typing to newNotification
mrjvs Jan 17, 2026
50e1479
feat: port userlist moderator port to JSX
mrjvs Jan 17, 2026
84d8a23
feat: port reports.ejs to JSX
mrjvs Jan 17, 2026
7032426
feat: port community management to JSX
mrjvs Jan 17, 2026
ea047b3
feat: port new community screen to JSX
mrjvs Jan 17, 2026
de71535
feat: port edit community view to JSX
mrjvs Jan 17, 2026
010cffd
feat: port moderate_user.ejs to JSX
mrjvs Jan 17, 2026
6da182a
fix: add SavePNID method to admin.js
mrjvs Jan 17, 2026
90cac15
chore: remove unused ejs files
mrjvs Jan 17, 2026
51d9c2d
fix: ignore sprites css
ashquarky Jan 18, 2026
9ff498d
fix: actually ignore generated spritesheet
ashquarky Jan 18, 2026
6d0dddc
fix: fixed calling grpc methods with non-string input
mrjvs Jan 18, 2026
9e41acf
fix: Fixed react warning
mrjvs Jan 18, 2026
223d75b
feat: add setting to bypass console checks
mrjvs Jan 18, 2026
d569072
fix: various fixes for problems I created
mrjvs Jan 18, 2026
0f29178
fix: image previews not being scoped in size
mrjvs Jan 18, 2026
c36d80a
fix: search not working on moderation pages
mrjvs Jan 18, 2026
3faacae
fix: next page button showing when it shouldnt
mrjvs Jan 18, 2026
7d3e9e3
fix: fixed searchbar styling
mrjvs Jan 18, 2026
d6fdeb0
fix: fixed types for parseReq files
mrjvs Jan 18, 2026
d987f6e
fix: fix undefined error in user check
mrjvs Jan 18, 2026
4c44a8a
fix: fixed left over ejs code in jsx
mrjvs Jan 18, 2026
094e5d6
fix: fix compile error
mrjvs Jan 18, 2026
e862a11
fix: Fixed react fragments not working. Replaced with different type …
mrjvs Jan 18, 2026
31a4d18
feat: port posts.js to ts
mrjvs Jan 18, 2026
f173eb5
feat: port error.ejs to jsx
mrjvs Jan 18, 2026
66b31eb
feat: port post.ejs to JSX
mrjvs Jan 18, 2026
879f582
feat: port fatal_error.ejs to JSX
mrjvs Jan 18, 2026
262b310
Merge pull request #288 from PretendoNetwork/work/sprites
ashquarky Jan 19, 2026
5b6ce3d
chore(deps-dev): bump eslint from 9.39.1 to 9.39.2
dependabot[bot] Jan 19, 2026
be86c22
chore(deps): bump react-dom from 19.2.0 to 19.2.3
dependabot[bot] Jan 19, 2026
1e8ee6c
Merge pull request #271 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Jan 20, 2026
b4c3227
Merge pull request #272 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Jan 20, 2026
9dcfd52
Merge pull request #273 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Jan 20, 2026
f04d921
chore(deps): bump tsx from 4.20.6 to 4.21.0
dependabot[bot] Jan 20, 2026
ea2c7f7
Merge pull request #286 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Jan 20, 2026
e24572f
Merge pull request #289 from PretendoNetwork/weblate
ashquarky Jan 20, 2026
b15d894
chore(ui): src/webfiles -> webfiles
ashquarky Jan 20, 2026
42fa43f
fix(ctr): ignore generated spritesheet
ashquarky Jan 20, 2026
9283683
chore: update nodemon for rename
ashquarky Jan 20, 2026
61d2079
chore: update build for webfiles rename
ashquarky Jan 20, 2026
3fd4f82
fix(ui): Move icon assets to view folder
ashquarky Jan 20, 2026
1d28dd2
feat(ui): give each target its own typescript context
ashquarky Jan 20, 2026
0e0ad7d
chore(deps): bump @pretendonetwork/grpc from 2.3.5 to 2.4.1
dependabot[bot] Jan 20, 2026
a5b16a1
chore(deps): bump supertest from 7.1.4 to 7.2.2
dependabot[bot] Jan 20, 2026
8eca7d2
locales(update): Updated Catalan locale
Jan 19, 2026
ff11bbb
Merge pull request #293 from PretendoNetwork/feat/admin-jsx
mrjvs Jan 20, 2026
861f130
Merge branch 'dev' into work/webfiles
mrjvs Jan 20, 2026
36c15cf
Merge pull request #297 from PretendoNetwork/work/webfiles
mrjvs Jan 20, 2026
2e95211
feat: port communities router to TS
mrjvs Jan 20, 2026
689e39d
feat: port notifications router to TS
mrjvs Jan 20, 2026
d8c0547
fix: cleanup endpoint data
mrjvs Jan 20, 2026
3ad81d7
fix: fixed incorrect naming of variables
mrjvs Jan 20, 2026
dfcd89e
Merge pull request #304 from PretendoNetwork/fix/clean-data
mrjvs Jan 20, 2026
ef58058
Merge branch 'dev' into feat/post-jsx
mrjvs Jan 21, 2026
b793f58
feat: port community list views to JSX
mrjvs Jan 21, 2026
cb204e5
chore: remove unused ejs files
mrjvs Jan 21, 2026
578233f
feat: port sub_communities.ejs to JSX
mrjvs Jan 21, 2026
f755c61
feat: port community view to JSX
mrjvs Jan 21, 2026
4ac2d41
chore: relocate permission logic to the router instead of the template
mrjvs Jan 21, 2026
4ddc76b
fix: fixed compile error, forgot to port something I relocated
mrjvs Jan 21, 2026
cbfdebe
fix: add closed post list view back
mrjvs Jan 21, 2026
fd58997
chore: clean up unsused ejs files
mrjvs Jan 21, 2026
d4db75b
chore(ctr): TypeScript for client stuff too
ashquarky Jan 18, 2026
51849a9
fix(ctr): use proper types for cave applet
ashquarky Jan 20, 2026
0228d32
feat(ctr): port XHR and deletePost to TS
ashquarky Jan 22, 2026
961f7f2
fix(ctr): refactor deletePost away from onclick=
ashquarky Jan 22, 2026
0c0cc2d
fix(ctr): override DELETE method
ashquarky Jan 22, 2026
f7ad030
chore(ctr): refactor delete and empathy endpoints clientside
ashquarky Jan 22, 2026
0c1e2d4
feat(portal): bring xhr/empathy/delete improvements to portal too
ashquarky Jan 22, 2026
3375ba6
fix(ctr): Use cleaner method of importing cave globals
ashquarky Jan 22, 2026
025739f
feat(web): bring xhr/empathy/delete improvements to web
ashquarky Jan 22, 2026
c13fcb0
fix(ui): use onload for xhr
ashquarky Jan 22, 2026
6c74ced
fix: error view not being centered
mrjvs Jan 23, 2026
e805324
fix: proper body parsing for post creation
mrjvs Jan 23, 2026
f8f9f93
fix: allow quests to view posts + fix userContent not being used
mrjvs Jan 23, 2026
3a51b85
fix: fixed styling of the yeah button on web
mrjvs Jan 23, 2026
2b65339
fix: solve crashes with metrics server on an empty database
mrjvs Jan 23, 2026
7af54b0
fix(ui): use onload for scroll to bottom event
ashquarky Jan 26, 2026
8165c9f
feat(ctr): add fallback for images that fail to load
ashquarky Jan 26, 2026
a755692
fix: use data-button-delete-post for delete post buttons
ashquarky Jan 26, 2026
7057172
feat(web): don't redirect moderators at all on post deletion
ashquarky Jan 26, 2026
815066b
chore: remove TODOs
mrjvs Jan 26, 2026
a1693a3
fix: improve conditional rendering logic in middleware
mrjvs Jan 26, 2026
d8bc83a
chore: add ctr error page for consoleAuth middleware
mrjvs Jan 26, 2026
b427a7e
fix: add a little more input validation to redirects
mrjvs Jan 26, 2026
42be109
chore: port fatal errors to jsxForDirectory
mrjvs Jan 28, 2026
d24afc8
Merge pull request #295 from PretendoNetwork/feat/post-jsx
mrjvs Jan 28, 2026
5c2482f
Merge branch 'dev' into patch/3ds-xhr
mrjvs Jan 28, 2026
efbf3a1
chore: update new JSX views with new delete method
mrjvs Jan 28, 2026
097000c
Merge pull request #306 from PretendoNetwork/patch/3ds-xhr
mrjvs Jan 28, 2026
e2c1814
chore(deps): bump esbuild from 0.25.4 to 0.27.2
dependabot[bot] Jan 28, 2026
6395787
chore(deps): bump ejs from 3.1.10 to 4.0.1
dependabot[bot] Jan 28, 2026
ff626c8
Merge pull request #298 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Jan 30, 2026
d92ba60
Merge pull request #302 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Jan 30, 2026
df9199e
Merge pull request #301 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Jan 30, 2026
dbb0b09
chore(deps): bump zod from 4.1.13 to 4.3.5
dependabot[bot] Jan 30, 2026
e62c163
Merge pull request #299 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Jan 30, 2026
6ccb91e
Merge pull request #300 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Jan 30, 2026
231369b
chore(deps): fix pixelsmith->get-pixels->request dependency
ashquarky Jan 31, 2026
7cd3d93
chore(deps): bump express stuff
ashquarky Jan 31, 2026
696b5ef
chore(deps): esbuild plugins are dev, unused colors
ashquarky Jan 31, 2026
419aa35
chore(deps): bump tsup's dependencies
ashquarky Jan 31, 2026
c83349e
chore(deps): bump qs/body-parser stuff
ashquarky Jan 31, 2026
df3b723
chore(deps): bump brace-expansion
ashquarky Jan 31, 2026
f1ff153
chore(deps): bump @aws-sdk/client-s3
ashquarky Jan 31, 2026
a0ef199
fix: fixd reports showing with deleted posts in JSX view
mrjvs Jan 31, 2026
adb7fdc
chore(deps-dev): bump ora from 9.0.0 to 9.1.0
dependabot[bot] Feb 2, 2026
d9bb86a
chore(deps): bump mongoose from 8.19.2 to 8.22.0
dependabot[bot] Feb 2, 2026
a8a04a7
chore(deps): bump react-dom from 19.2.3 to 19.2.4
dependabot[bot] Feb 2, 2026
4a5cf0e
Merge pull request #312 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Feb 2, 2026
9ccea6c
chore(deps-dev): bump newman from 6.2.1 to 6.2.2
dependabot[bot] Feb 2, 2026
e4b4211
Merge pull request #314 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Feb 2, 2026
7963218
chore(deps): bump @imagemagick/magick-wasm from 0.0.37 to 0.0.38
dependabot[bot] Feb 4, 2026
d7c899b
chore(deps-dev): bump ts-proto from 2.8.3 to 2.11.2
dependabot[bot] Feb 4, 2026
0e9f992
Merge pull request #310 from PretendoNetwork/fix/report-list
ashquarky Feb 5, 2026
4f6e300
Merge pull request #303 from PretendoNetwork/weblate
ashquarky Feb 5, 2026
0cb9f5d
fix(deps): nullability for parent community
ashquarky Feb 5, 2026
addefd8
Merge pull request #313 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Feb 5, 2026
1780515
Merge pull request #315 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Feb 5, 2026
f6293d2
Merge pull request #321 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Feb 5, 2026
469ed04
Merge pull request #322 from PretendoNetwork/dependabot/npm_and_yarn/…
ashquarky Feb 5, 2026
cb1c9d6
fix(ui): increase reply post limit
ashquarky Feb 5, 2026
871f64d
feat(ui): Add replies and removed posts to moderator feeds
ashquarky Feb 5, 2026
0d1b56b
feat(ui): Link timestamps to post
ashquarky Feb 5, 2026
976d460
feat(ui): Only show followers who aren't banned
ashquarky Feb 5, 2026
9b35c18
fix: allowed for empty strings in report db model
mrjvs Feb 6, 2026
7fc5eec
Merge pull request #323 from PretendoNetwork/patch/reply-limit
ashquarky Feb 6, 2026
d017395
Merge pull request #330 from PretendoNetwork/fix/report-no-reason
mrjvs Feb 7, 2026
d8d9c54
Merge pull request #324 from PretendoNetwork/feat/mod-feed-tweaks
mrjvs Feb 7, 2026
d70cdc2
fix: add index for community page post count
mrjvs Feb 7, 2026
6a156ba
Merge pull request #331 from PretendoNetwork/fix/community-post-index
mrjvs Feb 7, 2026
2ecd2b3
Merge branch 'master' into dev
mrjvs Feb 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ newman
config.json
uploads/

.docker/mitmproxy-data
.docker/mitmproxy-data
.DS_Store

# auto generated files
apps/juxtaposition-ui/webfiles/ctr/css/sprites.css
apps/juxtaposition-ui/webfiles/ctr/images/sprites.png
11 changes: 8 additions & 3 deletions apps/juxtaposition-ui/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ export default defineConfig([
rules: {
'no-restricted-imports': 'off' // It's a special compile step, we can't use path aliases
},
files: ['src/webfiles/**/*.js']
files: ['webfiles/**/*.js', 'webfiles/**/*.ts']
},
{
// Rules that apply to the 3DS (CTR) and Wii U (Portal) browsers
files: ['src/webfiles/ctr/**/*.js', 'src/webfiles/portal/**/*.js'],
files: [
'webfiles/ctr/**/*.js',
'webfiles/ctr/**/*.ts',
'webfiles/portal/**/*.js',
'webfiles/portal/**/*.ts'
],
rules: {
'no-var': 'off' // 3DS and Wii U browsers need to use var
},
Expand All @@ -46,6 +51,6 @@ export default defineConfig([
...globals.builtin
}
},
ignores: ['src/webfiles/**/*.js']
ignores: ['webfiles/**/*.js', 'webfiles/**/*.ts']
}
]);
7 changes: 6 additions & 1 deletion apps/juxtaposition-ui/nodemon.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
{
"watch": [
"src/"
"src/",
"webfiles/"
],
"ext": "js,ts,ejs,tsx,jsx,json,css",
"ignore": [
"webfiles/ctr/css/sprites.css",
"webfiles/ctr/images/sprites.png"
],
"env": {
"NODE_ENV": "development"
}
Expand Down
41 changes: 25 additions & 16 deletions apps/juxtaposition-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,52 +13,60 @@
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"build": "tsup && tsc --noEmit",
"start": "node --enable-source-maps dist/server.js"
"start": "node --enable-source-maps dist/server.js",
"build:sprites": "node scripts/sprites.ts"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.958.0",
"@imagemagick/magick-wasm": "^0.0.37",
"@aws-sdk/client-s3": "^3.980.0",
"@imagemagick/magick-wasm": "^0.0.38",
"@neato/config": "^4.1.0",
"@pretendonetwork/grpc": "^2.3.5",
"@pretendonetwork/grpc": "^2.4.1",
"@repo/grpc-client": "^0.0.0",
"classnames": "^2.5.1",
"colors": "^1.4.0",
"connect-redis": "^9.0.0",
"cookie-parser": "^1.4.7",
"crc": "^4.3.2",
"ejs": "^3.1.10",
"esbuild-plugin-copy": "^2.1.1",
"express": "^4.21.2",
"ejs": "^4.0.1",
"express": "^4.22.1",
"express-async-errors": "^3.1.1",
"express-prom-bundle": "^7.0.2",
"express-rate-limit": "^8.1.0",
"express-session": "^1.18.2",
"express-rate-limit": "^8.2.1",
"express-session": "^1.19.0",
"express-subdomain": "^1.0.6",
"hashmap": "^2.4.0",
"luxon": "^3.7.2",
"method-override": "^3.0.0",
"moment": "^2.30.1",
"mongoose": "^8.19.2",
"mongoose": "^8.22.0",
"mongoose-fuzzy-search-next": "^1.0.13",
"multer": "^2.0.2",
"nice-grpc": "^2.1.14",
"node-snowflake": "0.0.1",
"pako": "^2.1.0",
"pino": "^10.1.0",
"pino-http": "^11.0.0",
"pino-pretty": "^13.1.2",
"pino-pretty": "^13.1.3",
"pjax": "^0.2.8",
"prom-client": "^15.1.3",
"react": "^19.1.1",
"react-dom": "^19.2.0",
"react-dom": "^19.2.4",
"redis": "^5.10.0",
"tsx": "^4.20.6",
"zod": "^4.1.13"
"tsx": "^4.21.0",
"zod": "^4.3.6"
},
"devDependencies": {
"@pretendonetwork/cave-types": "^1.0.2",
"@pretendonetwork/eslint-config": "^0.1.3",
"@pretendonetwork/wiiu-browser-types": "^1.0.0",
"@pretendonetwork/wiiu-dialog-types": "^1.0.0",
"@pretendonetwork/wiiu-error-viewer-types": "^1.0.0",
"@repo/esbuild-plugin-oxipng": "^0.0.0",
"@repo/esbuild-plugin-spritesmith": "^0.0.0",
"@types/cookie-parser": "^1.4.10",
"@types/express-session": "^1.18.2",
"@types/hashmap": "^2.3.4",
"@types/luxon": "^3.7.1",
"@types/method-override": "^3.0.0",
"@types/node": "^22.19.1",
"@types/react": "^19.1.13",
"@types/react-dom": "^19.2.3",
Expand All @@ -67,7 +75,8 @@
"ejs-lint": "^2.0.1",
"esbuild-fix-imports-plugin": "^1.0.23",
"esbuild-raw-plugin": "^0.3.1",
"eslint": "^9.39.1",
"esbuild-plugin-copy": "^2.1.1",
"eslint": "^9.39.2",
"globals": "^16.5.0",
"nodemon": "^3.1.11",
"npm-run-all": "^4.1.5",
Expand Down
3 changes: 2 additions & 1 deletion apps/juxtaposition-ui/src/api/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,12 @@ export async function getPostsByEmpathy(tokens: UserTokens, empathy_by: number,
return posts;
}

export async function getPostsByParentId(tokens: UserTokens, parent_id: string, offset: number): Promise<PageDto<PostDto> | null> {
export async function getPostsByParentId(tokens: UserTokens, parent_id: string, offset: number, limit: number): Promise<PageDto<PostDto> | null> {
const posts = await apiFetchUser<PageDto<PostDto>>(tokens, `/api/v1/posts`, {
query: {
parent_id: parent_id,
offset,
limit,
include_replies: true,
sort: 'oldest'
}
Expand Down
3 changes: 3 additions & 0 deletions apps/juxtaposition-ui/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const schema = z.object({
whitelist: z.string(),
/** Environment (prod/test/dev) to use for Discovery and access_level control. */
serverEnvironment: z.string(),
/** By default, only consoles can reach the console domains. This setting disable that constraint */
disableConsoleChecks: z.stringbool().default(false),
/** The AES key to use for decrypting service tokens. Must match the account server's. */
aesKey: z.string(),
mongoose: z.object({
Expand Down Expand Up @@ -78,6 +80,7 @@ export const presets = {
cdnDomain: 'https://cdn.pretendo.cc/miiverse',
whitelist: '',
serverEnvironment: 'prod',
disableConsoleChecks: true,
aesKey: '1234567812345678123456781234567812345678123456781234567812345678',
mongoose: {
uri: 'mongodb://localhost:27017/miiverse?directConnection=true'
Expand Down
7 changes: 6 additions & 1 deletion apps/juxtaposition-ui/src/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ function verifyConnected() {
}
}

export function notBanned() {
return { account_status: { $in: [0, 1] } };
}

async function getCommunities(numberOfCommunities, offset) {
verifyConnected();
if (!offset) {
Expand Down Expand Up @@ -288,7 +292,8 @@ async function getUserContent(pid) {
async function getFollowingUsers(content) {
verifyConnected();
return SETTINGS.find({
pid: content.following_users
pid: content.following_users,
...notBanned()
});
}

Expand Down
19 changes: 12 additions & 7 deletions apps/juxtaposition-ui/src/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ import express from 'express';
import { logger } from '@/logger';
import { config } from '@/config';
import { SETTINGS } from '@/models/settings';
import type { Express } from 'express';
import type { Express, NextFunction, Request, Response } from 'express';

export const onlineNowGauge = new Gauge({
name: 'juxtaposition_online_users_now',
help: 'Users active in the last 10 minutes',
async collect(): Promise<void> {
const onlineRangeMs = 10 * 60 * 1000; // 10 minutes
const cutoff = new Date(Date.now() - onlineRangeMs);
const [{ n }] = await SETTINGS.aggregate<{ n: number }>([
const [result] = await SETTINGS.aggregate<{ n: number } | undefined>([
{ $match: { last_active: { $gt: cutoff } } },
{ $count: 'n' }
]);
this.set(n);
this.set(result?.n ?? 0);
}
});

Expand All @@ -26,11 +26,11 @@ export const activeMonthlyGauge = new Gauge({
async collect(): Promise<void> {
const monthlyRangeMs = 30 * 24 * 60 * 60 * 1000;
const cutoff = new Date(Date.now() - monthlyRangeMs);
const [{ n }] = await SETTINGS.aggregate<{ n: number }>([
const [result] = await SETTINGS.aggregate<{ n: number } | undefined>([
{ $match: { last_active: { $gt: cutoff } } },
{ $count: 'n' }
]);
this.set(n);
this.set(result?.n ?? 0);
}
});

Expand All @@ -40,11 +40,11 @@ export const activeYearlyGauge = new Gauge({
async collect(): Promise<void> {
const yearlyRangeMs = 365 * 24 * 60 * 60 * 1000;
const cutoff = new Date(Date.now() - yearlyRangeMs);
const [{ n }] = await SETTINGS.aggregate<{ n: number }>([
const [result] = await SETTINGS.aggregate<{ n: number } | undefined>([
{ $match: { last_active: { $gt: cutoff } } },
{ $count: 'n' }
]);
this.set(n);
this.set(result?.n ?? 0);
}
});

Expand Down Expand Up @@ -87,6 +87,11 @@ export function registerMetrics(app: Express): Express {
}));
}

metrics.use((error: Error, req: Request, res: Response, _next: NextFunction) => {
logger.error(error, 'Request failed!');
res.sendStatus(500);
});

return metrics;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import moment from 'moment';
import { database as db } from '@/database';
import { config } from '@/config';
import { humanDate, humanFromNow } from '@/util';
import { WebLoginView } from '@/services/juxt-web/views/web/loginView';
import { buildContext } from '@/services/juxt-web/views/context';
import { CtrFatalErrorView } from '@/services/juxt-web/views/ctr/errorView';
import { PortalFatalErrorView } from '@/services/juxt-web/views/portal/errorView';
import type { RequestHandler } from 'express';

export const checkBan: RequestHandler = async (request, response, next) => {
Expand Down Expand Up @@ -41,14 +45,13 @@ export const checkBan: RequestHandler = async (request, response, next) => {

if (!accessAllowed) {
response.status(500);
if (request.directory === 'web') {
return response.render('web/login.ejs', { toast: 'No access. Must be tester or dev', redirect: request.originalUrl });
} else {
return response.render('portal/error_fatal.ejs', {
code: 5989999,
message: 'No access. Must be tester or dev'
});
}
const banMessage = 'No access. Must be tester or dev';
const banCode = 5989999;
return response.jsxForDirectory({
web: <WebLoginView ctx={buildContext(response)} toast={banMessage} redirect={request.originalUrl} />,
portal: <PortalFatalErrorView code={banCode} message={banMessage} />,
ctr: <CtrFatalErrorView code={banCode} message={banMessage} />
});
}
const userSettings = await db.getUserSettings(request.pid);
if (userSettings && moment(userSettings.ban_lift_date) <= moment() && userSettings.account_status !== 3) {
Expand Down Expand Up @@ -79,17 +82,11 @@ export const checkBan: RequestHandler = async (request, response, next) => {
}
banMessage += `\n\nIf you have any questions, please contact the moderators on the Pretendo Network Forum (https://preten.do/ban-appeal/).`;

if (request.directory === 'web') {
return response.render('web/login.ejs', {
toast: banMessage,
redirect: request.originalUrl
});
} else {
return response.render(request.directory + '/error_fatal.ejs', {
message: banMessage,
code: banCode
});
}
return response.jsxForDirectory({
web: <WebLoginView ctx={buildContext(response)} toast={banMessage} redirect={request.originalUrl} />,
portal: <PortalFatalErrorView code={banCode} message={banMessage} />,
ctr: <CtrFatalErrorView code={banCode} message={banMessage} />
});
}

if (userSettings) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { config } from '@/config';
import { logger } from '@/logger';
import { CtrFatalErrorView } from '@/services/juxt-web/views/ctr/errorView';
import { PortalFatalErrorView } from '@/services/juxt-web/views/portal/errorView';
import { decodeParamPack, getPIDFromServiceToken, getUserAccountData, getUserDataFromToken, processLanguage } from '@/util';
import type { RequestHandler } from 'express';
import type { RequestHandler, Response } from 'express';

function renderAuthError(res: Response, code: number, message: string): void {
res.jsxForDirectory({
portal: <PortalFatalErrorView code={code} message={message} />,
ctr: <CtrFatalErrorView code={code} message={message} />
});
}

export const consoleAuth: RequestHandler = async (request, response, next) => {
// Get pid and fetch user data
Expand All @@ -24,11 +33,11 @@ export const consoleAuth: RequestHandler = async (request, response, next) => {
request.paramPackData = ppack ? decodeParamPack(ppack) : null;
response.header('X-Nintendo-WhiteList', config.whitelist);

if (!request.user) {
if (!request.user && request.cookies.access_token) {
try {
// Developer accounts may also use an OAuth token for console frontends
// Developer accounts may bypass console checks
const user = await getUserDataFromToken(request.cookies.access_token);
if (user.accessLevel === 3) {
if (user.accessLevel === 3 || config.disableConsoleChecks) {
request.user = await getUserAccountData(user.pid);
request.pid = user.pid;

Expand All @@ -50,32 +59,23 @@ export const consoleAuth: RequestHandler = async (request, response, next) => {
}
}

const mayBypassAuthChecks = request.user?.accessLevel === 3 || config.disableConsoleChecks;

// This section includes checks if a user is a developer and adds exceptions for these cases
if (!request.pid) {
return response.render('portal/error_fatal.ejs', {
code: 5989999,
message: 'Unable to parse service token. Are you using a Nintendo Network ID?'
});
return renderAuthError(response, 5989999, 'Unable to parse service token. Are you using a Nintendo Network ID?');
}
if (!request.user) {
return response.render('portal/error_fatal.ejs', {
code: 5989999,
message: 'Unable to fetch user data. Please try again later.'
});
return renderAuthError(response, 5989999, 'Unable to fetch user data. Please try again later.');
}
if (request.user.accessLevel < 3 && !request.paramPackData) {
return response.render('portal/error_fatal.ejs', {
code: 5989999,
message: 'Missing auth headers'
});

if (!mayBypassAuthChecks && !request.paramPackData) {
return renderAuthError(response, 5989999, 'Missing auth headers');
}
const userAgent = request.get('user-agent') ?? '';
const uaIsConsole = userAgent.includes('Nintendo WiiU') || userAgent.includes('Nintendo 3DS');
if (request.user.accessLevel < 3 && (request.cookies.access_token || !uaIsConsole)) {
return response.render('portal/error_fatal.ejs', {
code: 5989999,
message: 'Invalid authentication method used.'
});
if (!mayBypassAuthChecks && !uaIsConsole) {
return renderAuthError(response, 5989999, 'Invalid authentication method used.');
}

response.locals.uaIsConsole = uaIsConsole;
Expand Down
Loading