Skip to content

Commit fa9d612

Browse files
committed
Merge branch '2.2.x' for 2.2.0 release
2 parents e221eb8 + 53b07e9 commit fa9d612

File tree

82 files changed

+5821
-15528
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+5821
-15528
lines changed

.eslintrc.json

Lines changed: 0 additions & 15 deletions
This file was deleted.

.node-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
16
1+
22

.prettierignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Dependencies
2+
node_modules/
3+
package-lock.json
4+
5+
# Build output
6+
dist/
7+
build/
8+
9+
# Third-party libraries
10+
public/js/
11+
12+
# Logs
13+
*.log
14+
15+
# Database
16+
data/
17+
18+
# Docker
19+
Dockerfile
20+
docker-compose.yml
21+
22+
# Documentation that should preserve formatting
23+
DEVNOTES.md

.prettierrc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"tabWidth": 4,
3+
"useTabs": false,
4+
"semi": true,
5+
"singleQuote": true,
6+
"quoteProps": "as-needed",
7+
"trailingComma": false,
8+
"bracketSpacing": true,
9+
"bracketSameLine": false,
10+
"arrowParens": "avoid",
11+
"printWidth": 80,
12+
"endOfLine": "lf"
13+
}

CLAUDE.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
This is an rssCloud Server v2 implementation in Node.js - a notification protocol server that allows RSS feeds to notify subscribers when they are updated. The server handles subscription management and real-time notifications for RSS/feed updates.
8+
9+
## Development Commands
10+
11+
### Start Development
12+
13+
- `npm start` - Start server with nodemon (auto-reload on changes)
14+
- `npm run client` - Start client with nodemon
15+
16+
### Testing & Quality
17+
18+
- `npm test` - Run Mocha test suite (called by test-api)
19+
- `npm run test-api` - Run full API tests using Docker containers (MacOS tested)
20+
- `npm run lint` - Run ESLint with auto-fix on controllers/, services/, test/
21+
22+
### Data Management
23+
24+
- `npm run import-data` - Import data using bin/import-data.js
25+
26+
## Architecture
27+
28+
### Core Application Structure
29+
30+
- **app.js** - Main Express application entry point, sets up middleware, MongoDB connection, and starts server
31+
- **config.js** - Configuration management using nconf (env vars, CLI args, defaults)
32+
- **controllers/** - Express route handlers for API endpoints
33+
- **services/** - Business logic modules for core functionality
34+
- **views/** - Handlebars templates for web interface
35+
36+
### Key Services
37+
38+
- **services/mongodb.js** - MongoDB connection management with graceful shutdown
39+
- **services/notify-\*.js** - Notification system for subscribers
40+
- **services/ping.js** - RSS feed update detection and processing
41+
- **services/please-notify.js** - Subscription management
42+
43+
### API Endpoints (defined in controllers/index.js)
44+
45+
- `/pleaseNotify` - Subscribe to RSS feed notifications
46+
- `/ping` - Notify server of RSS feed updates
47+
- `/viewLog` - Event log viewer for debugging
48+
- `/RPC2` - XML-RPC endpoint
49+
- Web forms available at `/pleaseNotifyForm` and `/pingForm`
50+
51+
### Configuration
52+
53+
Environment variables (with defaults in config.js):
54+
55+
- `MONGODB_URI` (default: mongodb://localhost:27017/rsscloud)
56+
- `DOMAIN` (default: localhost)
57+
- `PORT` (default: 5337)
58+
- Resource limits: MAX_RESOURCE_SIZE, REQUEST_TIMEOUT, etc.
59+
60+
### Database
61+
62+
Uses MongoDB for storing subscriptions and resource state. Connection handled through services/mongodb.js with proper cleanup on shutdown.
63+
64+
### Testing
65+
66+
- Unit tests in test/ directory using Mocha/Chai
67+
- Docker-based API testing with mock endpoints
68+
- Test fixtures and SSL certificates in test/keys/

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:14 AS base
1+
FROM node:22 AS base
22

33
# Dockerize is needed to sync containers startup
44
ENV DOCKERIZE_VERSION v0.6.1

app.js

Lines changed: 94 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,103 @@
1-
(function () {
2-
"use strict";
3-
4-
require('dotenv').config();
5-
6-
const config = require('./config'),
7-
cors = require('cors'),
8-
express = require('express'),
9-
exphbs = require('express-handlebars'),
10-
fs = require('fs'),
11-
moment = require('moment'),
12-
mongodb = require('./services/mongodb'),
13-
morgan = require('morgan'),
14-
removeExpiredSubscriptions = require('./services/remove-expired-subscriptions');
15-
16-
let app,
17-
expressWs,
18-
hbs,
19-
server;
20-
21-
require('console-stamp')(console, 'HH:MM:ss.l');
1+
require('dotenv').config();
2+
3+
const config = require('./config'),
4+
cors = require('cors'),
5+
express = require('express'),
6+
exphbs = require('express-handlebars'),
7+
getDayjs = require('./services/dayjs-wrapper'),
8+
mongodb = require('./services/mongodb'),
9+
morgan = require('morgan'),
10+
{ setupLogRetention } = require('./services/log-cleanup'),
11+
removeExpiredSubscriptions = require('./services/remove-expired-subscriptions');
12+
13+
let app, hbs, server, dayjs;
14+
15+
console.log(`${config.appName} ${config.appVersion}`);
16+
17+
// Schedule cleanup tasks
18+
function scheduleCleanupTasks() {
19+
// Run subscription cleanup every 24 hours
20+
setInterval(async() => {
21+
try {
22+
console.log('Running scheduled subscription cleanup...');
23+
await removeExpiredSubscriptions();
24+
} catch (error) {
25+
console.error('Error in scheduled subscription cleanup:', error);
26+
}
27+
}, 24 * 60 * 60 * 1000); // 24 hours in milliseconds
28+
}
2229

23-
console.log(`${config.appName} ${config.appVersion}`);
30+
morgan.format('mydate', () => {
31+
return new Date().toLocaleTimeString('en-US', { hour12: false, fractionalSecondDigits: 3 }).replace(/:/g, ':');
32+
});
2433

25-
// TODO: Every 24 hours run removeExpiredSubscriptions(data);
34+
// Initialize dayjs at startup
35+
async function initializeDayjs() {
36+
dayjs = await getDayjs();
37+
}
2638

27-
morgan.format('mydate', function() {
28-
var df = require('dateformat');
29-
return df(new Date(), 'HH:MM:ss.l');
30-
});
39+
app = express();
3140

32-
app = express();
33-
expressWs = require('express-ws')(app);
41+
app.use(morgan('[:mydate] :method :url :status :res[content-length] - :remote-addr - :response-time ms'));
3442

35-
app.use(morgan('[:mydate] :method :url :status :res[content-length] - :remote-addr - :response-time ms'));
43+
app.use(cors());
3644

37-
app.use(cors());
45+
// Configure handlebars template engine to work with dayjs
46+
hbs = exphbs.create({
47+
helpers: {
48+
formatDate: (datetime, format) => {
49+
return dayjs(datetime).format(format);
50+
}
51+
}
52+
});
53+
54+
// Configure express to use handlebars
55+
app.engine('handlebars', hbs.engine);
56+
app.set('view engine', 'handlebars');
57+
58+
// Handle static files in public directory
59+
app.use(express.static('public', {
60+
dotfiles: 'ignore',
61+
maxAge: '1d'
62+
}));
63+
64+
// Load controllers
65+
app.use(require('./controllers'));
66+
67+
// Start server
68+
async function startServer() {
69+
await initializeDayjs();
70+
await mongodb.connect('rsscloud', config.mongodbUri);
71+
72+
// Setup log retention TTL index
73+
try {
74+
await setupLogRetention();
75+
} catch (error) {
76+
console.error('Failed to setup log retention, continuing without it:', error);
77+
}
78+
79+
// Start cleanup scheduling
80+
scheduleCleanupTasks();
81+
82+
server = app.listen(config.port, () => {
83+
app.locals.host = config.domain;
84+
app.locals.port = server.address().port;
85+
86+
if (app.locals.host.indexOf(':') > -1) {
87+
app.locals.host = '[' + app.locals.host + ']';
88+
}
3889

39-
// Configure handlebars template engine to work with moment
40-
hbs = exphbs.create({
41-
helpers: {
42-
formatDate: function (datetime, format) {
43-
return moment(datetime).format(format);
90+
console.log(`Listening at http://${app.locals.host}:${app.locals.port}`);
91+
})
92+
.on('error', (error) => {
93+
switch (error.code) {
94+
case 'EADDRINUSE':
95+
console.log(`Error: Port ${config.port} is already in use.`);
96+
break;
97+
default:
98+
console.log(error.code);
4499
}
45-
}
46-
});
47-
48-
// Configure express to use handlebars
49-
app.engine('handlebars', hbs.engine);
50-
app.set('view engine', 'handlebars');
51-
52-
// Handle static files in public directory
53-
app.use(express.static('public', {
54-
dotfiles: 'ignore',
55-
maxAge: '1d'
56-
}));
57-
58-
// Load controllers
59-
app.use(require('./controllers'));
60-
61-
// Start server
62-
mongodb.connect('rsscloud', config.mongodbUri)
63-
.then(() => {
64-
server = app.listen(config.port, function () {
65-
app.locals.host = config.domain;
66-
app.locals.port = server.address().port;
67-
68-
if (app.locals.host.indexOf(':') > -1) {
69-
app.locals.host = '[' + app.locals.host + ']';
70-
}
71-
72-
console.log('Listening at http://%s:%s', app.locals.host, app.locals.port);
73-
})
74-
.on('error', function (error) {
75-
switch (error.code) {
76-
case 'EADDRINUSE':
77-
console.log(`Error: Port ${config.port} is already in use.`);
78-
break;
79-
default:
80-
console.log(error.code);
81-
}
82-
});
83100
});
84-
}());
101+
}
102+
103+
startServer().catch(console.error);

0 commit comments

Comments
 (0)