Skip to content

Commit

Permalink
First
Browse files Browse the repository at this point in the history
  • Loading branch information
joenas committed Sep 28, 2021
0 parents commit e1b8b74
Show file tree
Hide file tree
Showing 23 changed files with 3,644 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.git
.gitignore
node_modules/
docker-compose.yml
Dockerfile
README.md
test
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
### REQUIRED

TZ=Europe/Stockholm
REDIS_URL=redis://localhost:6379

### OPTIONAL

# Ping to make sure cron is alive, for example https://healthchecks.io or https://deadmanssnitch.com
#PING_URL_CRON=http://localhost:4567/ping

21 changes: 21 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
env: {
browser: true,
commonjs: true,
es6: true,
},
extends: ['airbnb-base', 'prettier'],
plugins: ['prettier'],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
},
parserOptions: {
ecmaVersion: 2018,
},
rules: {
'prettier/prettier': 'error',
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'func-names': ['error', 'never'],
},
};
34 changes: 34 additions & 0 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: Node.js CI

on:
push:
branches: [master]
pull_request:
branches: [master]

env:
CI: true

jobs:
server:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
working-directory: server
run: |
yarn install --frozen-lockfile
- name: ESLint
working-directory: server
run: |
yarn lint
67 changes: 67 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

# 0x
profile-*

# mac files
.DS_Store

# vim swap files
*.swp

# webstorm
.idea

# vscode
*code-workspace

# clinic
profile*
*clinic*
*flamegraph*

# generated code
examples/typescript-server.js
test/types/index.js

# environment
.env

# config
config/reddit.yml
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "all"
}
6 changes: 6 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
]
}
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"editor.formatOnSave": true,
"eslint.run": "onSave",
"files.insertFinalNewline": true,
}
1 change: 1 addition & 0 deletions CHECKS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/checks online
15 changes: 15 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM node:14-slim
RUN mkdir -p /app
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
ENV PORT=5000
ENV NODE_ENV=production

COPY ["package.json", "yarn.lock", "./"]
RUN yarn install --silent --frozen-lockfile

COPY ["Procfile","CHECKS", "./"]
COPY ["src/", "./src"]

EXPOSE 5000
CMD [ "yarn", "start" ]
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: yarn start
1 change: 1 addition & 0 deletions Procfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: yarn dev | pino-pretty
8 changes: 8 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "Top Reddit",
"description": "Fetch top scored posts from Reddit and emit",
"keywords": ["dokku", "node", "reddit"],
"scripts": {
"dokku": {}
}
}
6 changes: 6 additions & 0 deletions config/reddit.yml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
- name: 'worldnews'
url: 'https://www.reddit.com/r/worldnews.json?limit=1'
minCount: 15
webhookUrl: 'https://example.com'
displayName: WorldNews
14 changes: 14 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: '3.2'
services:
cron:
build:
context: .
dockerfile: Dockerfile
env_file: ./.env

redis:
image: microbox/redis:2.8.17
ports:
- 6379:6379
volumes:
- /tmp/redis:/data
36 changes: 36 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "node-top-reddit",
"version": "1.0.0",
"repository": "https://github.com/joenas/node-top-reddit",
"author": "Jon Neverland",
"license": "MIT",
"private": true,
"main": "src/cron.js",
"scripts": {
"dev": "./node_modules/.bin/nodemon ./src/cron.js",
"start": "node ./src/cron.js",
"lint:eslint": "eslint --ignore-path .gitignore",
"lint:eslint:fix": "eslint --ignore-path .gitignore --fix .",
"lint": "npm run lint:eslint -- . "
},
"keywords": [],
"dependencies": {
"cron": "^1.8.2",
"dotenv": "^8.2.0",
"node-fetch": "^2.6.1",
"pino": "^6.7.0",
"redis": "^4.0.0-rc.2",
"yaml": "^2.0.0-8"
},
"devDependencies": {
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.1.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-prettier": "^3.1.3",
"nodemon": "^2.0.5",
"pino-pretty": "^4.3.0",
"prettier": "^2.0.5",
"tap": "^12.5.3"
}
}
21 changes: 21 additions & 0 deletions src/cron.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require('dotenv').config();
const { CronJob } = require('cron');
const fetch = require('node-fetch');
const logger = require('./logger')('cron');
const fetchFeeds = require('./fetch-feeds');

// Feeds
// eslint-disable-next-line no-new
new CronJob(
'*/15 7-23 * * *',
// '*/1 * * * *',
async function () {
logger.info("Triggering 'Fetch feeds'");
await fetchFeeds();
const url = process.env.PING_URL_CRON;
if (url) await fetch(url);
},
null,
true,
process.env.TZ,
);
41 changes: 41 additions & 0 deletions src/fetch-feeds.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const fs = require('fs');
const YAML = require('yaml');
const request = require('./request');
const redis = require('./redis');
const webhook = require('./webhook');

const matchingPosts = async (url, minCount) => {
const res = await request(url);
const posts = res.data.children;
return posts.filter((p) => p.data.score >= minCount).map((p) => p.data);
};

const postWebhook = async (webhookUrl, displayName, post) => {
const { url, title, permalink } = post;
const body = {
text: `<a href='${url}'>${title}</a> (<a href='https://www.reddit.com${permalink}'>comments</a>)`,
format: 'html',
displayName: displayName || 'Reddit',
// avatarUrl: "",
msgtype: 'notice',
};
await webhook({ url: webhookUrl, body });
};

const fetchFeeds = async () => {
const cache = await redis();
const configs = YAML.parse(fs.readFileSync('./config/reddit.yml', 'utf8'));

configs.forEach(async (conf) => {
const { name, url, minCount, displayName, webhookUrl } = conf;
const posts = await matchingPosts(url, minCount);
posts.forEach(async (post) => {
const exists = await cache.exists(name, post.url);
if (exists) return;
await cache.add(name, post.url);
await postWebhook(webhookUrl, displayName, post);
});
});
};

module.exports = fetchFeeds;
6 changes: 6 additions & 0 deletions src/logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const pino = require('pino');

module.exports = (name) =>
pino({
name,
});
21 changes: 21 additions & 0 deletions src/redis.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const redis = require('redis');
const logger = require('./logger')('redis');

module.exports = async () => {
const client = redis.createClient({ url: process.env.REDIS_URL });

client.on('error', (err) => {
logger.error(err);
process.exit(1);
});
await client.connect();

return {
exists: async (key, value) => {
return client.SISMEMBER(key, value);
},
add: async (key, value) => {
return client.SADD(key, value);
},
};
};
40 changes: 40 additions & 0 deletions src/request.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const fetch = require('node-fetch');
const { URL, URLSearchParams } = require('url');

/**
* Move to proper shared for client and server?
*/

const parseJSON = (response) => {
if (response.status === 204 || response.status === 205) {
return null;
}
return response.json();
};

const checkStatus = (response) => {
const { status, statusText } = response;
/** Return response for validation errors as well */
if ((status >= 200 && status < 300) || status === 400) {
return response;
}
const error = new Error(statusText);
error.response = response;
throw error;
};

const request = (url, options = {}) => {
const uri = new URL(url);
if (options.params) {
uri.search = new URLSearchParams(options.params).toString();
}
return fetch(uri, {
...options,
headers: { 'content-type': 'application/json' },
})
.then(checkStatus)
.then(parseJSON);
};

module.exports = request;
module.exports.checkStatus = checkStatus;
Loading

0 comments on commit e1b8b74

Please # to comment.