├── .changeset
├── README.md
└── config.js
├── .eslintrc.json
├── .github
└── workflows
│ └── nodejs.yml
├── .gitignore
├── .vscode
└── settings.json
├── Procfile
├── README.md
├── doczrc.json
├── examples
├── basic
│ ├── CHANGELOG.md
│ ├── index.js
│ └── package.json
├── minimum
│ ├── CHANGELOG.md
│ ├── index.js
│ └── package.json
└── secure
│ ├── CHANGELOG.md
│ ├── index.js
│ └── package.json
├── guides
├── deployToHeroku.md
├── diagram.svg
└── gettingStarted.md
├── netlify.toml
├── package.json
├── packages
├── S3
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── package.json
│ └── readme.mdx
├── core
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── package.json
│ └── readme.mdx
├── dropbox
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── package.json
│ └── readme.mdx
├── fileSystem
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── package.json
│ └── readme.mdx
└── services
│ ├── dribbble
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── package.json
│ └── readme.mdx
│ ├── github
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── package.json
│ └── readme.mdx
│ ├── nasa
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── package.json
│ └── readme.mdx
│ ├── twitter
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── package.json
│ └── readme.mdx
│ └── youtube
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── package.json
│ └── readme.mdx
├── welcome.mdx
└── yarn.lock
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with `bolt` to help you release components from a mono-repository. You can find the full documentation for it [here](https://www.npmjs.com/package/@changesets/cli)
4 |
5 | To help you get started though, here are some things you should know about this folder:
6 |
7 | ## Changesets are automatically generated
8 |
9 | Changesets are generated by the `yarn changeset` or `npx changeset` command. As long as you are following a changeset release flow, you shouldn't have any problems.
10 |
11 | ## Each changeset is its own folder
12 |
13 | We use hashes by default for these folder names to avoid collisions when generating them, but there's no harm that will come from renaming them.
14 |
15 | ## Changesets are automatically removed
16 |
17 | When `changeset bump` or equivalent command is run, all the changeset folders are removed. This is so we only ever use a changeset once. This makes this a very bad place to store any other information.
18 |
19 | ## Changesets come in two parts
20 |
21 | You should treat these parts quite differently:
22 |
23 | - `changes.md` is a file you should feel free to edit as much as you want. It will be prepended to your changelog when you next run your version command.
24 | - `changes.json` is a file that includes information about releases, what should be versioned by the version command. We strongly recommend against editing this directly, as you may make a new changeset that puts your bolt repository into an invalid state.
25 |
26 | ## I want to edit the information in a `changes.json` - how do I do it safely?
27 |
28 | The best option is to make a new changeset using the changeset command, copy over the `changes.md`, then delete the old changeset.
29 |
30 | ## Can I rename the folder for my changeset?
31 |
32 | Absolutely! We need unique hashes to make changesets play nicely with git, but changing your folder from our hash to your own name isn't going to cause any problems.
33 |
34 | ## Can I manually delete changesets?
35 |
36 | You can, but you should be aware this will remove the intent to release communicated by the changeset, and should be done with caution.
37 |
--------------------------------------------------------------------------------
/.changeset/config.js:
--------------------------------------------------------------------------------
1 | /*
2 | Hey, welcome to the changeset config! This file has been generated
3 | for you with the default configs we use, and some comments around
4 | what options mean, so that it's easy to customise your workflow.
5 |
6 | You should update this as you need to craft your workflow.
7 |
8 | Config provided by a CI command takes precedence over the contents of this file.
9 |
10 | If a config option isn't present here, we will fall back to the defaults.
11 | */
12 |
13 | const changesetOptions = {
14 | // If true, we will automatically commit the changeset when the command is run
15 | commit: false
16 | };
17 |
18 | // This function takes information about a changeset to generate an entry for it in your
19 | // changelog. We provide the full changeset object as well as the version.
20 | // It may be a good idea to replace the commit hash with a link to the commit.
21 |
22 | /* the default shape is:
23 | ### Bump Type
24 |
25 | - GIT_HASH: A summary message you wrote, indented?
26 | */
27 |
28 | const getReleaseLine = async (changeset, type) => {
29 | const [firstLine, ...futureLines] = changeset.summary
30 | .split("\n")
31 | .map(l => l.trimRight());
32 |
33 | return `- ${changeset.commit}: ${firstLine}\n${futureLines
34 | .map(l => ` ${l}`)
35 | .join("\n")}`;
36 | };
37 |
38 | // This function takes information about what dependencies we are updating in the package.
39 | // It provides an array of related changesets, as well as the dependencies updated.
40 |
41 | /*
42 | - Updated dependencies: [ABCDEFG]:
43 | - Updated dependencies: [HIJKLMN]:
44 | - dependencyA@1.0.1
45 | - dependencyb@1.2.0
46 | */
47 | const getDependencyReleaseLine = async (changesets, dependenciesUpdated) => {
48 | if (dependenciesUpdated.length === 0) return "";
49 |
50 | const changesetLinks = changesets.map(
51 | changeset => `- Updated dependencies [${changeset.commit}]:`
52 | );
53 |
54 | const updatedDepenenciesList = dependenciesUpdated.map(
55 | dependency => ` - ${dependency.name}@${dependency.version}`
56 | );
57 |
58 | return [...changesetLinks, ...updatedDepenenciesList].join("\n");
59 | };
60 |
61 | const versionOptions = {
62 | // If true, we will automatically commit the version updating when the command is run
63 | commit: false,
64 | // Adds a skipCI flag to the commit - only valid if `commit` is also true.
65 | skipCI: false,
66 | // Do not modify the `changelog.md` files for packages that are updated
67 | updateChangelog: true,
68 | // A function that returns a string. It takes in options about a change. This allows you to customise your changelog entries
69 | getReleaseLine,
70 | // A function that returns a string. It takes in options about when a pacakge is updated because
71 | getDependencyReleaseLine,
72 | // An array of arrays that defines packages that are linked.
73 | // Linked packages are packages that should be at the same version when they're released.
74 | // If you've used Lerna to version packages before, this is very similar.
75 | linked: [[]]
76 | };
77 |
78 | const publishOptions = {
79 | // This sets whether unpublished packages are public by default. We err on the side of caution here.
80 | public: false
81 | };
82 |
83 | module.exports = {
84 | versionOptions,
85 | changesetOptions,
86 | publishOptions
87 | };
88 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": false,
4 | "es6": true
5 | },
6 | "extends": ["airbnb-base", "prettier"],
7 | "globals": {
8 | "Atomics": "readonly",
9 | "SharedArrayBuffer": "readonly"
10 | },
11 | "parserOptions": {
12 | "ecmaVersion": 2018,
13 | "sourceType": "module"
14 | },
15 | "rules": {
16 | "no-console": 0
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | name: Node CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | strategy:
10 | matrix:
11 | node-version: [8.x, 10.x, 12.x]
12 |
13 | steps:
14 | - uses: actions/checkout@v1
15 | - name: Use Node.js ${{ matrix.node-version }}
16 | uses: actions/setup-node@v1
17 | with:
18 | node-version: ${{ matrix.node-version }}
19 | - name: npm install, build, and test
20 | run: |
21 | yarn
22 | yarn lint:eslint
23 | env:
24 | CI: true
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /test
3 | .env
4 | .docz
5 | .DS_store
6 | *.log
7 | node_modules
8 | package-lock.json
9 | cache
10 | scratchpad.txt
11 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "workbench.colorCustomizations": {
3 | "titleBar.activeBackground": "#5d2aea",
4 | "statusBar.background": "#5d2aea",
5 | "activityBar.background": "#5d2aea"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node examples/basic/index.js
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ISOBEL 🐶
2 |
3 |
4 | Welcome!
5 | Isobel is a beginner friendly NodeJS framework for fetching data from your social profiles and other sources, for display in your own apps and websites. Isobel periodically fetches data from the services that you teach her, so the data is always there when you need it.
6 |
7 |
8 | A great example is Twitter, which gets the latest posts from your Twitter feed. Once configured, you can access the JSON for your latest tweets at a defined URL, so you can display them on your website. That means no worrying about API rate-limiting or quotas, and no making API calls from your frontend (a big no-no).
9 |
10 | Isobel includes a bunch of premade services, but you can also create your own.
11 |
12 | Here is a minimum implementation of Isobel.
13 |
14 | ```javascript
15 | const ISOBEL = require("@isobel/core");
16 | const fileSystem = require("@isobel/file-system");
17 | const nasa = require("@isobel/nasa");
18 | const toMs = require("to-ms");
19 |
20 | // initialise
21 | const Isobel = new ISOBEL({
22 | cache: fileSystem,
23 | services: [
24 | {
25 | name: "nasa",
26 | func: nasa.fetchPhotoOfTheDay, // gets the NASA photo of the day
27 | interval: toMs.hours(24)
28 | }
29 | ]
30 | });
31 |
32 | Isobel.start().catch(error => {
33 | console.error("Error:", error);
34 | process.exit(1);
35 | });
36 | ```
37 |
38 | Once Isobel is up and running, you can access your data in a JSON at a specific URL. e.g `localhost:4000/get/nasa`
39 |
40 | ```json
41 | {
42 | "url": "https://apod.nasa.gov/apod/image/1911/jC-L-TM_SunFinal5HRweb1024.jpg",
43 | "hdurl": "https://apod.nasa.gov/apod/image/1911/jC-L-TM_SunFinal5HRweb.jpg",
44 | "explanation": "On November 11, 2019 the Sun was mostly quiet, experiencing a minimum in its 11 year cycle of activity. In fact, the only spot v..."
45 | }
46 | ```
47 |
48 | Ready to go? head to the [Getting Started guide](https://isobeljs.com/guides-getting-started) to...well...get started!
49 |
50 | ## Supported services
51 |
52 | Isobel supports many services out of the box, here is the full list
53 |
54 | ### Dribbble
55 |
56 | Retrieves your latest shots from Dribbble
57 |
58 | ### GitHub
59 |
60 | Retrives profile information from GitHub
61 |
62 | ### NASA
63 |
64 | Retrives the NASA photo of the day. Treat this one as an example for you to create your own services!
65 |
66 | ### Twitter
67 |
68 | Retrives your latest tweets!
69 |
70 | ### YouTube
71 |
72 | Retrives information about your YouTube Channel
73 |
74 | ## Supported caching strategies
75 |
76 | Isobel currently supports three caching strategies, with more on the way!
77 |
78 | - local file system
79 | - Dropbox
80 | - Amazon S3 Buckets
81 |
82 | ## Acknowledgements
83 |
84 | Isobel is created by [Nathan Simpson](https://nathansimpson.design). Thank you to Thomas Walker for your inspiration, support and friendship.
85 |
--------------------------------------------------------------------------------
/doczrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Isobel 🐶",
3 | "description": "A beginner friendly NodeJS framework for fetching data from your social profiles and other sources, to display in your own apps and websites.",
4 | "themeConfig": {
5 | "colors": {
6 | "primary": "#5d2aea"
7 | },
8 | "styles": {
9 | "h1": "color: #5d2aea; font-size: 4em"
10 | },
11 | "files": "**/*.{md,markdown,mdx}",
12 | "ignore": ["scratchpad.md"]
13 | },
14 | "menu": ["Welcome", "Getting Started", "Guides", "Packages", "Services"]
15 | }
16 |
--------------------------------------------------------------------------------
/examples/basic/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # isobel-basic-example
2 |
3 | ## 0.2.2
4 |
5 | ### Patch Changes
6 |
7 | - : Moved one line
8 |
9 | ## 0.2.1
10 |
11 | - Updated dependencies []:
12 | - @isobel/s3@0.2.0
13 |
14 | ## 0.2.0
15 |
16 | ### Minor Changes
17 |
18 | - fc715b8: Initial release!
19 |
--------------------------------------------------------------------------------
/examples/basic/index.js:
--------------------------------------------------------------------------------
1 | const ISOBEL = require("@isobel/core");
2 |
3 | // utils
4 | const fileSystem = require("@isobel/file-system");
5 | const S3 = require("@isobel/s3");
6 | const toMs = require("to-ms");
7 |
8 | // services
9 | const nasa = require("@isobel/nasa");
10 | const twitter = require("@isobel/twitter");
11 | const dribbble = require("@isobel/dribbble");
12 | const youtube = require("@isobel/youtube");
13 | const github = require("@isobel/github");
14 |
15 | const services = [
16 | {
17 | name: "dribbble",
18 | func: dribbble.fetchLatestShots,
19 | interval: toMs.hours(24),
20 | params: {
21 | accessToken: process.env.DRIBBBLE_ACCESS_TOKEN
22 | }
23 | },
24 | {
25 | name: "twitter",
26 | func: twitter.fetchLatestTweets,
27 | interval: toMs.hours(6),
28 | params: {
29 | username: "nathjsimpson"
30 | }
31 | },
32 | {
33 | name: "nasa",
34 | func: nasa.fetchPhotoOfTheDay,
35 | interval: toMs.hours(24)
36 | },
37 | {
38 | name: "youtube",
39 | func: youtube.getChannelStats,
40 | interval: toMs.hours(24),
41 | params: {
42 | channelId: "UCa__hNMzVWIQOHErctX0leg"
43 | }
44 | },
45 | {
46 | name: "github",
47 | func: github.getProfile,
48 | interval: toMs.hours(24)
49 | }
50 | ];
51 |
52 | // initialise
53 | const Isobel = new ISOBEL({
54 | cache: process.env.NODE_ENV === "production" ? S3 : fileSystem,
55 | services
56 | });
57 |
58 | Isobel.start().catch(error => {
59 | console.error("Error:", error);
60 | process.exit(1);
61 | });
62 |
--------------------------------------------------------------------------------
/examples/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "isobel-basic-example",
3 | "private": true,
4 | "version": "0.2.2",
5 | "author": "Nathan Simpson",
6 | "scripts": {
7 | "start": "node index.js"
8 | },
9 | "dependencies": {
10 | "@isobel/core": "^0.1.0",
11 | "@isobel/file-system": "^0.1.3",
12 | "@isobel/s3": "^0.2.0",
13 | "@isobel/nasa": "^0.1.0",
14 | "@isobel/twitter": "^0.1.0",
15 | "@isobel/dribbble": "^0.1.0",
16 | "@isobel/youtube": "^0.1.0",
17 | "@isobel/github": "^0.1.0",
18 | "to-ms": "1.2.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/minimum/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # isobel-minimum-example
2 |
3 | ## 0.2.0
4 | ### Minor Changes
5 |
6 | - fc715b8: Initial release!
7 |
--------------------------------------------------------------------------------
/examples/minimum/index.js:
--------------------------------------------------------------------------------
1 | const ISOBEL = require("@isobel/core");
2 | const fileSystem = require("@isobel/file-system");
3 | const nasa = require("@isobel/nasa");
4 |
5 | // initialise
6 | const Isobel = new ISOBEL({
7 | cache: fileSystem,
8 | services: [
9 | {
10 | name: "nasa",
11 | func: nasa.fetchPhotoOfTheDay,
12 | interval: 24 * 60 * 60 * 1000
13 | }
14 | ]
15 | });
16 |
17 | Isobel.start().catch(error => {
18 | console.error("Error:", error);
19 | process.exit(1);
20 | });
21 |
--------------------------------------------------------------------------------
/examples/minimum/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "isobel-minimum-example",
3 | "private": true,
4 | "version": "0.2.0",
5 | "author": "Nathan Simpson",
6 | "scripts": {
7 | "start": "node index.js"
8 | },
9 | "dependencies": {
10 | "@isobel/core": "^0.1.0",
11 | "@isobel/file-system": "^0.1.0",
12 | "@isobel/nasa": "^0.1.0"
13 | }
14 | }
--------------------------------------------------------------------------------
/examples/secure/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # isobel-secure-example
2 |
3 | ## 0.2.0
4 | ### Minor Changes
5 |
6 | - fc715b8: Initial release!
7 |
--------------------------------------------------------------------------------
/examples/secure/index.js:
--------------------------------------------------------------------------------
1 | const ISOBEL = require("@isobel/core");
2 | const fileSystem = require("@isobel/file-system");
3 | const nasa = require("@isobel/nasa");
4 | const toMs = require("to-ms");
5 |
6 | // initialise
7 | const Isobel = new ISOBEL({
8 | cache: fileSystem,
9 | services: [
10 | {
11 | name: "nasa",
12 | func: nasa.fetchPhotoOfTheDay,
13 | interval: toMs.hours(24)
14 | }
15 | ],
16 | security: {
17 | allowedIPs: ["127.0.0.1"] // only serve requests from these IP addresses
18 | }
19 | });
20 |
21 | Isobel.start().catch(error => {
22 | console.error("Error:", error);
23 | process.exit(1);
24 | });
25 |
--------------------------------------------------------------------------------
/examples/secure/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "isobel-secure-example",
3 | "private": true,
4 | "version": "0.2.0",
5 | "author": "Nathan Simpson",
6 | "scripts": {
7 | "start": "node index.js"
8 | },
9 | "dependencies": {
10 | "@isobel/core": "^0.1.0",
11 | "@isobel/file-system": "^0.1.0",
12 | "@isobel/nasa": "^0.1.0",
13 | "to-ms": "1.2.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/guides/deployToHeroku.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Deploy to Heroku
3 | menu: Guides
4 | ---
5 |
6 | # Deploy to Heroku
7 |
8 | Heroku is a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud. It's great for beginners to get their apps running quickly on the internet, perfect for Isobel apps.
9 |
10 | ## Get started
11 |
12 | First, create a [GitHub](https://www.github.com) account, and a new Repository for your project. Call it whatever you like (e.g. isobel-app).
13 |
14 | Run through the Getting Started guide, as well as the docs for cloud-compatible caching strategies like [Dropbox](https://isobeljs.com/packages-dropbox-readme) or [Amazon S3](https://isobeljs.com/packages-s3-readme). Note that the 'File System' caching strategy is incompatible with Heroku. We recommend taking a look at the ['basic' example](https://github.com/nathsimpson/isobel/blob/master/examples/basic/index.js), which is ready to go with Heroku.
15 |
16 | Once you are happy, push this code to GitHub.
17 |
18 | ## Heroku-specific steps
19 |
20 | Ok, now that we are done dev-ing on our local machine, let's get this onto the internets!
21 |
22 | Head to [heroku.com](https://heroku.com), and sign up for a free account. Then, create a new app. Call it whatever you like and choose a region that is close to you.
23 |
24 | Under Deployment Method, choose GitHub and link your GitHub account to Heroku. Then, choose the GitHub repository with your Isobel app in it. Once connected, click 'Enable Automatic Deploys'. This means your Isobel app will update every time you push to your Master branch on GitHub.
25 |
26 | We now have to specify a 'buildpack', which is the software Heroku wil use to run our app. Go to Settings > Buildpacks, and tap 'Node.js'. We need to re-deploy to start using the Buildpack, so head to Deploy > 'Deploy Branch', and wait for deploy to complete.
27 |
28 | All good? Click 'Open App' in the top right corner. Do you get a Error 404? That's fine! add `/welcome` to the end of the URL. You will get the following...
29 |
30 | ```
31 | Hello. My name is ISOBEL
32 | ```
33 |
34 | The system works! 🎉
35 |
36 | Head to `/nasa`, and you should information for the NASA Photo of the Day.
37 |
38 | ## Mission Complete
39 |
40 | Congratulations!! You have successfully deployed a Isobel app to the Internet. That's massive. You should be super proud.
41 |
42 | Where to from here? Now the world is your oyster. You can add one of the premade Isobel services available, or even create your own! Then, it's time to use that data an display it on your website. Guides for that are outside the scope for these docs, but if your app performs a `fetch` request to the Isobel URL for your service, you can then start creating objects in your apps.
43 |
44 | Happy hacking!
45 |
--------------------------------------------------------------------------------
/guides/diagram.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/guides/gettingStarted.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Getting Started
3 | ---
4 |
5 | # Getting Started
6 |
7 | Follow these steps to get your first Isobel up and running.
8 |
9 | ## Install Node
10 |
11 | The first step is to [install NodeJS](https://nodesource.com/blog/installing-nodejs-tutorial-mac-os-x/) on your machine. Once you have followed the guide, type the following into your terminal to verify Node is installed.
12 |
13 | ```
14 | node -v
15 | ```
16 |
17 | This should return something like `v10.16.0` in the terminal.
18 |
19 | ## Initialise Isobel
20 |
21 | Ok, let's get this party started. First, create a new folder on your desktop called 'myIsobelApp' (or whatever you like). Then, open your text editor (we like Visual Studio Code), and open the 'myIsobelApp' folder within it.
22 |
23 | Create a `package.json` file, and copy this code into it.
24 |
25 | ```json
26 | {
27 | "name": "my-isobel-app",
28 | "version": "1.0.0",
29 | "description": "Look ma, I'm on the internet!",
30 | "main": "index.js",
31 | "scripts": {
32 | "start": "node index.js"
33 | },
34 | "author": "your name here"
35 | }
36 | ```
37 |
38 | Now. Let's install Isobel. Run the following snippit in your terminal to install
39 |
40 | ```
41 | yarn add @isobel/core @isobel/file-system @isobel/nasa
42 | ```
43 |
44 | Then, create an `index.js` file, and copy this code into it.
45 |
46 | ```javascript
47 | // import the packages
48 | const ISOBEL = require("@isobel/core");
49 | const fileSystem = require("@isobel/file-system");
50 | const nasa = require("@isobel/nasa");
51 | const toMs = require("to-ms");
52 |
53 | // initialise
54 | const Isobel = new ISOBEL({
55 | cache: fileSystem, // where the data will be saved and read from
56 | services: [
57 | {
58 | name: "nasa", // the name of the cache artifact for the URL
59 | func: nasa.fetchPhotoOfTheDay, // which function will be run
60 | interval: toMs.hours(24) // how often the cache will be refreshed
61 | }
62 | ]
63 | });
64 |
65 | Isobel.start().catch(error => {
66 | console.error("Error:", error);
67 | process.exit(1);
68 | });
69 | ```
70 |
71 | Excellent! Running `yarn start` will now produce the following...
72 |
73 | ```
74 | 🐶 ISOBEL listening on port 4000.
75 | ⚙️ Starting caching
76 | ✅ saved nasa to file
77 | ```
78 |
79 | Awesome! Now head to `localhost:4000/get/nasa` to see the results!
80 |
81 | Congratulations, you are up and running.
82 |
83 | ## Next steps
84 |
85 | Now that you've got Isobel going. Take a look at the [other services](https://isobeljs.com/#supported-services) that are available for use, or maybe you can create your own? Take a look at the [NASA service](https://github.com/nathsimpson/isobel/blob/master/packages/services/nasa/index.js) to see what goes into making one.
86 |
87 | Once you are ready to get onto the internet, check out the docs for [Deploying to Heroku](https://isobeljs.com/guides-deploy-to-heroku).
88 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | # COMMENT: This a rule for Single Page Applications as Docz site is one
2 | [[redirects]]
3 | from = "/*"
4 | to = "/"
5 | status = 200
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "isobel-monorepo",
3 | "description": "A beginner friendly NodeJS framework for fetching data from your social profiles and other sources, to display in your own apps and websites.",
4 | "private": true,
5 | "workspaces": [
6 | "examples/*",
7 | "packages/*",
8 | "!packages/services",
9 | "packages/services/*"
10 | ],
11 | "scripts": {
12 | "start": "node examples/basic/index.js",
13 | "docz:dev": "docz dev",
14 | "docz:build": "docz build",
15 | "test": "jest",
16 | "format": "prettier --write \"**/*.js{,on}\" \"**/*.md\"",
17 | "lint:eslint": "eslint ."
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/nathsimpson/isobel.git"
22 | },
23 | "author": "Nathan Simpson",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/nathsimpson/isobel/issues"
27 | },
28 | "homepage": "https://github.com/nathsimpson/isobel#readme",
29 | "babel": {
30 | "presets": [
31 | "es2015",
32 | "stage-2"
33 | ],
34 | "plugins": []
35 | },
36 | "nodemonConfig": {
37 | "ignore": [
38 | "lib/cache/*.json"
39 | ]
40 | },
41 | "devDependencies": {
42 | "@changesets/cli": "^1.3.0",
43 | "babel-cli": "^6.26.0",
44 | "babel-polyfill": "^6.26.0",
45 | "babel-preset-es2015": "^6.24.1",
46 | "babel-preset-stage-2": "^6.24.1",
47 | "docz": "^1.2.0",
48 | "docz-theme-default": "^1.2.0",
49 | "eslint": "^5.16.0",
50 | "eslint-config-airbnb-base": "^13.1.0",
51 | "eslint-config-prettier": "^4.3.0",
52 | "eslint-plugin-import": "^2.14.0",
53 | "jest": "^24.8.0",
54 | "prettier": "1.18.2",
55 | "to-ms": "1.2.0"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/packages/S3/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @isobel/s3
2 |
3 | ## 0.2.0
4 |
5 | ### Minor Changes
6 |
7 | - : Enables user to specify bucket name
8 |
9 | ## 0.1.0
10 |
11 | ### Minor Changes
12 |
13 | - fc715b8: Initial release!
14 |
--------------------------------------------------------------------------------
/packages/S3/index.js:
--------------------------------------------------------------------------------
1 | const AWS = require("aws-sdk");
2 |
3 | const s3 = new AWS.S3({ signatureVersion: "v4" });
4 | const Bucket = process.env.AWS_BUCKET_NAME;
5 |
6 | exports.save = async (endpoint, data) =>
7 | new Promise((resolve, reject) => {
8 | s3.putObject(
9 | {
10 | Bucket,
11 | Key: `cache/${endpoint}.json`,
12 | Body: JSON.stringify(data)
13 | },
14 | err => {
15 | if (err) {
16 | console.log("❌ error saving to s3", err);
17 | return reject();
18 | }
19 | console.log(`✅ saved ${endpoint} to s3`);
20 | return resolve();
21 | }
22 | );
23 | });
24 |
25 | exports.read = async endpoint =>
26 | new Promise((resolve, reject) => {
27 | s3.getObject(
28 | {
29 | Bucket,
30 | Key: `cache/${endpoint}.json`
31 | },
32 | (err, res) => {
33 | if (err) {
34 | console.log("> error retrieving to s3", err);
35 | return reject();
36 | }
37 | return resolve(JSON.parse(res.Body));
38 | }
39 | );
40 | });
41 |
--------------------------------------------------------------------------------
/packages/S3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@isobel/s3",
3 | "version": "0.2.0",
4 | "author": "Nathan Simpson",
5 | "license": "MIT",
6 | "dependencies": {
7 | "aws-sdk": "^2.346.0"
8 | }
9 | }
--------------------------------------------------------------------------------
/packages/S3/readme.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: S3
3 | menu: Packages
4 | ---
5 |
6 | # @isobel/s3
7 |
8 | Enables Isobel to interface with an S3 bucket for reading and writing cache artifacts.
9 |
10 | ## Creating your Amazon S3 Bucket.
11 |
12 | Create a free account at with Amazon Web Services([aws.amazon.com](https://aws.amazon.com/)), then go to [Amazon S3](s3.console.aws.amazon.com) and create your first bucket.
13 | Name the bucket whatever you like. You can leave everything else as default or tweak to your liking.
14 |
15 | Specify the name of your S3 Bucket in `.env` file
16 |
17 | ```
18 | AWS_BUCKET_NAME=XXXXXXX
19 | ```
20 |
21 | ## Get your S3 API Keys
22 |
23 | Once your bucket is created, we need to give Isobel a way of being able to read/write securely, using API keys.
24 | There are two ways we can go here, we can either use the API keys for your admin account (which is less secure as they have full access to all of your Amazon AWS services), or we can create a API key specific for Isobel (this is more complex but is way more secure).
25 |
26 | ### The quick way.
27 |
28 | WARNING: This method uses API keys that have full access to all of your Amazon AWS services. This is not the most secure approach, so proceed at your own risk.
29 | While logged into Amazon S3, click your username at the top-right of the screen, and click 'My Security Credentials'. Underneath Access keys, create a new pair of access keys. Copy these keys into your `.env` file under the following keys...
30 |
31 | ```
32 | AWS_ACCESS_KEY_ID=XXXXXXX
33 | AWS_SECRET_ACCESS_KEY=XXXXXXX
34 | ```
35 |
36 | ### The secure way (recommended)
37 |
38 | The preferred method of giving Isobel access to S3 is to create unique AWS API keys for Isobel, with access permissions scoped to the correct S3 bucket.
39 |
40 | #### Create an Access Policy
41 |
42 | A policy is a specific permission that you grant to a user. In this case, we want to only grant permissions to read/write to out S3 bucket.
43 | While logged in to AWS, go to services > IAM, and then click 'Policies' in the sidebar. Create a new Policy using these following settings...
44 | Services - Add 'S3'
45 | Actions - tick 'GetObject', 'PutObject' and 'DeleteObject'
46 | Resources - expand the menu. In the 'Object' section, press 'Add ARN', Enter the following...
47 |
48 | - Bucket Name: Type in the name of your S3 Bucket. (e.g. `arn:aws:s3:::my-isobel`).
49 | - Object Name: Tick the 'Any' box.
50 |
51 | In the next scene, name your policy 'AccessIsobel', and save it.
52 |
53 | #### Create an IAM User
54 |
55 | While logged in to AWS, go to services > IAM, and then click 'Users' in the sidebar.
56 | Create a new user, and name it whatever you like (e.g. my-isobel). Tick the box that says 'programmatic access', and click next.
57 | The next scene is permissions, click 'Attach existing policies directly', and attach the 'AccessIsobel' policy you just created.
58 |
59 | Skip the 'tags' scene, and review/save the new IAM user. In the next scene, copy the Access Key ID and Secret Access Key, and paste them in your `.env` file.
60 |
61 | ```
62 | AWS_ACCESS_KEY_ID=XXXXXXX
63 | AWS_SECRET_ACCESS_KEY=XXXXXXX
64 | ```
65 |
66 | You will not be able to see these credentials again in the AWS interface, but you can generate new ones later if they get forgotten.
67 |
68 | ## Configure Isobel to use S3
69 |
70 | Finally, we have to tell Isobel to use S3 as the caching strategy.
71 |
72 | ```javascript
73 | const ISOBEL = require("@isobel/core");
74 | const S3 = require("@isobel/s3");
75 | // import your services here
76 |
77 | const services = [
78 | // initialise your services in here
79 | ];
80 |
81 | const Isobel = new ISOBEL({
82 | cache: S3,
83 | services
84 | });
85 |
86 | Isobel.start().catch(error => {
87 | console.error("Error:", error);
88 | process.exit(1);
89 | });
90 | ```
91 |
92 | If you would like to, you could also choose to run the filesystem cache while in development.
93 |
94 | ```javascript
95 | const Isobel = new ISOBEL({
96 | cache: process.env.NODE_ENV === "production" ? S3 : fileSystem,
97 | services
98 | });
99 | ```
100 |
101 | ## Expected env vars
102 |
103 | S3 interface will not work unless these environment variables are specified.
104 |
105 | - AWS_BUCKET_NAME
106 | - AWS_ACCESS_KEY_ID
107 | - AWS_SECRET_ACCESS_KEY
108 |
--------------------------------------------------------------------------------
/packages/core/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @isobel/core
2 |
3 | ## 0.1.0
4 | ### Minor Changes
5 |
6 | - fc715b8: Initial release!
7 |
--------------------------------------------------------------------------------
/packages/core/index.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const cors = require("cors");
3 | const morgan = require("morgan");
4 | const dotenv = require("dotenv");
5 | const bodyParser = require("body-parser");
6 | const CFonts = require("cfonts");
7 |
8 | const port = process.env.PORT || 4000;
9 |
10 | dotenv.load();
11 |
12 | const cacheEndpoint = async (service, cache) => {
13 | const { name, func, params } = service;
14 | await func(params)
15 | .then(data => cache.save(name, data))
16 | .catch(err => console.log(`❌ ERROR caching ${name} -`, err.message));
17 | };
18 |
19 | const startCaching = (service, cache) => {
20 | cacheEndpoint(service, cache);
21 | setInterval(() => cacheEndpoint(service, cache), service.interval);
22 | };
23 |
24 | module.exports = class ISOBEL {
25 | constructor(config) {
26 | this.express = express;
27 | this.app = express();
28 | this.app.use(cors());
29 | this.app.use(morgan("tiny"));
30 | this.app.use(bodyParser.json()); // support json encoded bodies
31 | this.app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
32 | this.config = config;
33 | }
34 |
35 | async start() {
36 | const {
37 | app,
38 | config: { cache, services, security }
39 | } = this;
40 |
41 | CFonts.say("ISOBEL", {
42 | font: "3d", // define the font face
43 | align: "left", // define text alignment
44 | colors: ["yellow", "cyan"] // define all colors
45 | });
46 |
47 | console.log(`🐶 ISOBEL listening on port ${port}.`);
48 |
49 | return new Promise((resolve, reject) => {
50 | app.get("/", (req, res) => {
51 | res.status(404).end();
52 | });
53 |
54 | app.get("/welcome", (req, res) => res.send("Hello. My name is ISOBEL"));
55 |
56 | app.get("/get/:service/", async (req, res) => {
57 | const { service } = req.params;
58 | const requestIp = req.headers["x-forwarded-for"];
59 |
60 | if (!service) return res.status(404).end();
61 |
62 | if (
63 | requestIp &&
64 | security &&
65 | security.allowedIPs &&
66 | security.allowedIPs.indexOf(requestIp) === -1
67 | ) {
68 | return res.status(403).end();
69 | }
70 |
71 | return res.json(await cache.read(service));
72 | });
73 |
74 | app.listen(port, error => {
75 | console.log("⚙️ Starting caching");
76 |
77 | if (error) return reject(error);
78 |
79 | services.forEach(service => startCaching(service, cache));
80 |
81 | return resolve({ port });
82 | });
83 | });
84 | }
85 | };
86 |
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@isobel/core",
3 | "version": "0.1.0",
4 | "license": "MIT",
5 | "author": "Nathan Simpson",
6 | "dependencies": {
7 | "cfonts": "^2.4.3",
8 | "express": "^4.16.3",
9 | "morgan": "^1.9.1",
10 | "cors": "^2.8.4",
11 | "dotenv": "^6.1.0",
12 | "body-parser": "^1.18.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/core/readme.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Core
3 | menu: Packages
4 | ---
5 |
6 | # @isobel/core
7 |
8 | ## Expected env vars
9 |
10 | - PORT, specifies the PORT on which Isobel will run (default 4000)
11 |
--------------------------------------------------------------------------------
/packages/dropbox/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @isobel/dropbox
2 |
3 | ## 0.1.0
4 | ### Minor Changes
5 |
6 | - ad16875: Created Dropbox package
7 |
--------------------------------------------------------------------------------
/packages/dropbox/index.js:
--------------------------------------------------------------------------------
1 | const fetch = require("isomorphic-fetch");
2 | const { Dropbox } = require("dropbox");
3 |
4 | const dbx = new Dropbox({
5 | accessToken: process.env.DROPBOX_ACCESS_TOKEN,
6 | fetch
7 | });
8 |
9 | exports.save = async (endpoint, data) =>
10 | new Promise((resolve, reject) => {
11 | dbx
12 | .filesUpload({
13 | path: `/${endpoint}.json`,
14 | contents: JSON.stringify(data)
15 | })
16 | .then(() => {
17 | console.log(`✅ saved ${endpoint} to Dropbox`);
18 | return resolve();
19 | })
20 | .catch(err => {
21 | console.log("error", err);
22 | return reject(new Error({ message: err.response.data }));
23 | });
24 | });
25 |
26 | exports.read = async endpoint =>
27 | new Promise(resolve => {
28 | dbx
29 | .filesDownload({ path: `/${endpoint}.json` })
30 | .then(response => {
31 | return resolve(JSON.parse(response.fileBinary.toString("utf8")));
32 | })
33 | .catch(err => {
34 | throw err;
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/packages/dropbox/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@isobel/dropbox",
3 | "version": "0.1.0",
4 | "license": "MIT",
5 | "author": "Nathan Simpson",
6 | "dependencies": {
7 | "dropbox": "^4.0.27",
8 | "isomorphic-fetch": "^2.2.1"
9 | }
10 | }
--------------------------------------------------------------------------------
/packages/dropbox/readme.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Dropbox
3 | menu: Packages
4 | ---
5 |
6 | # @isobel/dropbox
7 |
8 | Enables Isobel to use Dropbox for reading/writing cache data.
9 |
10 | ## Implementing Dropbox caching
11 |
12 | To implement Dropbox, run `yarn add @isobel/dropbox` in your project directory, and then import it in your project like this...
13 |
14 | ```javascript
15 | const ISOBEL = require("@isobel/core");
16 | const dropbox = require("@isobel/dropbox");
17 |
18 | const Isobel = new ISOBEL({
19 | cache: dropbox,
20 | services: [] // your list of services here
21 | });
22 |
23 | Isobel.start().catch(error => {
24 | console.error("Error:", error);
25 | process.exit(1);
26 | });
27 | ```
28 |
29 | ## Getting your Dropbox tokens
30 |
31 | In order to connect Isobel with your Dropbox account, you will need to generate the correct API tokens. Here are the steps to achieve this.
32 |
33 | 1. Create a new app at https://www.dropbox.com/developers/apps
34 | 2. Choose App Folder, name it "Isobel"
35 | 3. Copy the App Key, App Secret and 0Auth Access Token. Store them in your .env file using the correct keys (see below in 'Expected env vars')
36 |
37 | ## Expected env vars
38 |
39 | Ensure these keys exist in your .env file, in order for Isobel to work with Dropbox.
40 |
41 | - DROPBOX_KEY
42 | - DROPBOX_SECRET
43 | - DROPBOX_ACCESS_TOKEN
44 |
--------------------------------------------------------------------------------
/packages/fileSystem/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @isobel/file-system
2 |
3 | ## 0.1.3
4 |
5 | ### Patch Changes
6 |
7 | - : Fixed broken path
8 |
9 | ## 0.1.2
10 |
11 | ### Patch Changes
12 |
13 | - : Bug fixes
14 |
15 | ## 0.1.1
16 |
17 | ### Patch Changes
18 |
19 | - : FS now creates cache folder automatically
20 |
21 | ## 0.1.0
22 |
23 | ### Minor Changes
24 |
25 | - fc715b8: Initial release!
26 |
--------------------------------------------------------------------------------
/packages/fileSystem/index.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 |
4 | const dir = path.join(__dirname, `/../../../cache`);
5 |
6 | exports.save = async (endpoint, data) =>
7 | new Promise(resolve => {
8 | const filePath = `${dir}/${endpoint}.json`;
9 | if (!fs.existsSync(dir)) {
10 | fs.mkdirSync(dir);
11 | }
12 | fs.writeFileSync(filePath, JSON.stringify(data));
13 | console.log(`✅ saved ${endpoint} to file`);
14 | return resolve();
15 | });
16 |
17 | exports.read = async endpoint =>
18 | new Promise(resolve => {
19 | const filePath = `${dir}/${endpoint}.json`;
20 | return resolve(JSON.parse(fs.readFileSync(filePath, "utf-8")));
21 | });
22 |
--------------------------------------------------------------------------------
/packages/fileSystem/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@isobel/file-system",
3 | "version": "0.1.3",
4 | "license": "MIT",
5 | "author": "Nathan Simpson"
6 | }
--------------------------------------------------------------------------------
/packages/fileSystem/readme.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: FileSystem
3 | menu: Packages
4 | ---
5 |
6 | # @isobel/file-system
7 |
8 | Enables Isobel to interface with your local file system for reading and writing cache artifacts.
9 |
--------------------------------------------------------------------------------
/packages/services/dribbble/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @isobel/dribbble
2 |
3 | ## 0.1.0
4 | ### Minor Changes
5 |
6 | - fc715b8: Initial release!
7 |
--------------------------------------------------------------------------------
/packages/services/dribbble/index.js:
--------------------------------------------------------------------------------
1 | const axios = require("axios");
2 |
3 | exports.fetchLatestShots = async params => {
4 | const result = await axios(
5 | `https://api.dribbble.com/v2/user/shots?access_token=${params.accessToken}`
6 | ).catch(err => {
7 | throw new Error(
8 | `${err.response.statusText}. ${err.response.data &&
9 | err.response.data.message}`
10 | );
11 | });
12 |
13 | return result.data.map(shot => ({
14 | description: shot.description.replace(/(<([^>]+)>)/gi, ""),
15 | image: shot.images.normal,
16 | link: shot.html_url
17 | }));
18 | };
19 |
--------------------------------------------------------------------------------
/packages/services/dribbble/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@isobel/dribbble",
3 | "version": "0.1.0",
4 | "author": "Nathan Simpson",
5 | "license": "MIT",
6 | "dependencies": {
7 | "axios": "^0.18.0"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/services/dribbble/readme.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Dribbble
3 | menu: Services
4 | ---
5 |
6 | # @isobel/dribbble
7 |
8 | ## `fetchLatestShots`
9 |
10 | Returns the latest shots from the authenticated account.
11 |
12 | ### Expected env vars
13 |
14 | - DRIBBBLE_ACCESS_TOKEN
15 |
--------------------------------------------------------------------------------
/packages/services/github/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @isobel/github
2 |
3 | ## 0.1.0
4 | ### Minor Changes
5 |
6 | - fc715b8: Initial release!
7 |
--------------------------------------------------------------------------------
/packages/services/github/index.js:
--------------------------------------------------------------------------------
1 | const axios = require("axios");
2 |
3 | const accessToken = process.env.GITHUB_ACCESS_TOKEN;
4 |
5 | exports.getProfile = async () => {
6 | const result = await axios({
7 | url: "https://api.github.com/graphql",
8 | method: "post",
9 | headers: { Authorization: `bearer ${accessToken}` },
10 | data: {
11 | query: `
12 | query {
13 | viewer {
14 | login
15 | name
16 | bio
17 | company
18 | email
19 | id
20 | location
21 | repositories(first: 100, privacy:PUBLIC){
22 | nodes {
23 | id
24 | name
25 | url
26 | }
27 | }
28 | }
29 | }
30 | `
31 | }
32 | }).catch(err => {
33 | throw new Error(err.response.statusText);
34 | });
35 |
36 | return result.data;
37 | };
38 |
--------------------------------------------------------------------------------
/packages/services/github/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@isobel/github",
3 | "version": "0.1.0",
4 | "author": "Nathan Simpson",
5 | "license": "MIT",
6 | "dependencies": {
7 | "axios": "^0.18.0"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/services/github/readme.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: GitHub
3 | menu: Services
4 | ---
5 |
6 | # @isobel/github
7 |
8 | ## `getProfile`
9 |
10 | Returns information about the authenticated GitHub user
11 |
12 | ### Expected env vars
13 |
14 | - GITHUB_ACCESS_TOKEN
15 |
--------------------------------------------------------------------------------
/packages/services/nasa/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @isobel/nasa
2 |
3 | ## 0.1.0
4 | ### Minor Changes
5 |
6 | - fc715b8: Initial release!
7 |
--------------------------------------------------------------------------------
/packages/services/nasa/index.js:
--------------------------------------------------------------------------------
1 | const axios = require("axios");
2 |
3 | exports.fetchPhotoOfTheDay = async () => {
4 | const result = await axios
5 | .get("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")
6 | .catch(err => {
7 | throw new Error(err.response.statusText);
8 | });
9 |
10 | const { url, hdurl, explanation } = result.data;
11 |
12 | return {
13 | url,
14 | hdurl,
15 | explanation
16 | };
17 | };
18 |
--------------------------------------------------------------------------------
/packages/services/nasa/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@isobel/nasa",
3 | "version": "0.1.0",
4 | "author": "Nathan Simpson",
5 | "license": "MIT",
6 | "dependencies": {
7 | "axios": "^0.18.0"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/services/nasa/readme.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Nasa
3 | menu: Services
4 | ---
5 |
6 | # @isobel/nasa
7 |
8 | The NASA service is an example of a minimum implementation of an Isobel service. It is available to guide you on creating your own services.
9 | [View on GitHub](https://github.com/nathsimpson/isobel/tree/master/packages/services/nasa).
10 |
11 | ## Actions
12 |
13 | ### .fetchPhotoOfTheDay
14 |
15 | Retrieves information on the NASA Photo of the fetchPhotoOfTheDay
16 |
17 | ```
18 | {
19 | "url":"https://apod.nasa.gov/apod/image/....jpg",
20 | "hdurl":"https://apod.nasa.gov/apod/image/....jpg",
21 | "explanation":"Description goes here"}
22 | ```
23 |
--------------------------------------------------------------------------------
/packages/services/twitter/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @isobel/twitter
2 |
3 | ## 0.1.0
4 | ### Minor Changes
5 |
6 | - fc715b8: Initial release!
7 |
--------------------------------------------------------------------------------
/packages/services/twitter/index.js:
--------------------------------------------------------------------------------
1 | const Twit = require("twit");
2 |
3 | const T = new Twit({
4 | consumer_key: process.env.TWITTER_CONSUMER_KEY,
5 | consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
6 | access_token: process.env.TWITTER_ACCESS_TOKEN,
7 | access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET,
8 | timeout_ms: 60 * 1000, // optional HTTP request timeout to apply to all requests.
9 | strictSSL: true // optional - requires SSL certificates to be valid.
10 | });
11 |
12 | exports.fetchLatestTweets = async params => {
13 | // check if all params are set
14 | ["username"].forEach(key => {
15 | if (!params[key]) {
16 | throw new Error(`${key} is not set in params`);
17 | }
18 | });
19 |
20 | const final = await T.get("statuses/user_timeline", {
21 | screen_name: params.username,
22 | count: 10,
23 | exclude_replies: true,
24 | include_rts: false
25 | }).catch(err => {
26 | throw new Error(err.message);
27 | });
28 |
29 | return final.data.map(tweet => ({
30 | id: tweet.id,
31 | text: tweet.text,
32 | place: tweet.place ? tweet.place.full_name : null,
33 | image: tweet.entities.media ? tweet.entities.media[0].display_url : null
34 | }));
35 | };
36 |
--------------------------------------------------------------------------------
/packages/services/twitter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@isobel/twitter",
3 | "version": "0.1.0",
4 | "author": "Nathan Simpson",
5 | "license": "MIT",
6 | "dependencies": {
7 | "twit": "^2.2.11"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/services/twitter/readme.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Twitter
3 | menu: Services
4 | ---
5 |
6 | # @isobel/twitter
7 |
8 | ## Actions
9 |
10 | ### .fetchLatestTweets
11 |
12 | Returns an array of your latest Tweets
13 |
14 | #### Params
15 |
16 | `username`: The username of the account to retrieve tweets from
17 |
18 | ## Getting your API Key
19 |
20 | Head to [developer.twitter.com/apps](https://developer.twitter.com/apps), and create a new Twitter App. Once your app is created, click on 'Keys and Tokens', and paste all of the values in your .env file under the following keys...
21 |
22 | ## Expected env vars
23 |
24 | - TWITTER_CONSUMER_KEY
25 | - TWITTER_CONSUMER_SECRET
26 | - TWITTER_ACCESS_TOKEN
27 | - TWITTER_ACCESS_TOKEN_SECRET
28 |
--------------------------------------------------------------------------------
/packages/services/youtube/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @isobel/youtube
2 |
3 | ## 0.1.0
4 | ### Minor Changes
5 |
6 | - fc715b8: Initial release!
7 |
--------------------------------------------------------------------------------
/packages/services/youtube/index.js:
--------------------------------------------------------------------------------
1 | const axios = require("axios");
2 |
3 | const apiKey = process.env.YOUTUBE_API_KEY;
4 |
5 | exports.getChannelStats = async params => {
6 | // check if all params are set
7 | ["channelId"].forEach(key => {
8 | if (!params[key]) {
9 | throw new Error(`${key} is not set in params`);
10 | }
11 | });
12 |
13 | const result = await axios(
14 | `https://www.googleapis.com/youtube/v3/channels?part=statistics&id=${params.channelId}&key=${apiKey}`
15 | ).catch(err => {
16 | throw new Error(err.response.statusText);
17 | });
18 |
19 | return result.data.items[0].statistics;
20 | };
21 |
--------------------------------------------------------------------------------
/packages/services/youtube/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@isobel/youtube",
3 | "version": "0.1.0",
4 | "author": "Nathan Simpson",
5 | "license": "MIT",
6 | "dependencies": {
7 | "axios": "^0.18.0"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/services/youtube/readme.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: YouTube
3 | menu: Services
4 | ---
5 |
6 | # @isobel/youtube
7 |
8 | ## Actions
9 |
10 | ### .getChannelStats
11 |
12 | Retrieves an object of information about the specified YouTube channel.
13 |
14 | #### Returns
15 |
16 | ```json
17 | {
18 | "viewCount": "132273",
19 | "commentCount": "0",
20 | "subscriberCount": "238",
21 | "hiddenSubscriberCount": false,
22 | "videoCount": "13"
23 | }
24 | ```
25 |
26 | #### Params
27 |
28 | `channelId`: The ID of the YouTube channel (copied from the channel URL).
29 |
30 | ## Getting your API Key
31 |
32 | 1. developers.google.com
33 | 2. Create new project
34 | 3. Enable YouTube API v3
35 | 4. Create credentials -> API Key, webserver(node.js), public data
36 |
37 | Please note that usage of this service will incur a point of usage from the Google Cloud API.
38 |
39 | ## Expected env vars
40 |
41 | YouTube service will not work unless these environment variables are specified.
42 |
43 | - YOUTUBE_API_KEY = Your API Key, generated by creating a new project at developers.google.com.
44 | - YOUTUBE_CHANNEL_ID = The ID of your YouTube channel. Retrievable from the URL of your channel page.
45 |
--------------------------------------------------------------------------------
/welcome.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Welcome
3 | route: "/"
4 | ---
5 |
6 | # ISOBEL 🐶
7 |
8 |
9 | Welcome! Isobel is a beginner friendly NodeJS framework for fetching data from
10 | your social profiles and other sources, for display in your own apps and
11 | websites.
12 |
13 |
14 | 
15 |
16 | Isobel periodically fetches data from the services that you teach
17 | her, so the data is always there when you need it.
18 |
19 | A great example is Twitter, which gets the latest posts from your Twitter feed. Once configured, you can access the JSON for your latest tweets at a defined URL, so you can display them on your website. That means no worrying about API rate-limiting or quotas, and no making API calls from your frontend (a big no-no).
20 |
21 | Isobel includes a bunch of premade services, but you can also create your own.
22 |
23 | ## Minimum Implementation
24 |
25 | ```javascript
26 | const ISOBEL = require("@isobel/core");
27 | const fileSystem = require("@isobel/file-system");
28 | const nasa = require("@isobel/nasa");
29 | const toMs = require("to-ms");
30 |
31 | // initialise
32 | const Isobel = new ISOBEL({
33 | cache: fileSystem,
34 | services: [
35 | {
36 | name: "nasa",
37 | func: nasa.fetchPhotoOfTheDay, // gets the NASA photo of the day
38 | interval: toMs.hours(24)
39 | }
40 | ]
41 | });
42 |
43 | Isobel.start().catch(error => {
44 | console.error("Error:", error);
45 | process.exit(1);
46 | });
47 | ```
48 |
49 | Once Isobel is up and running, you can access your data in a JSON at a specific URL. e.g `localhost:4000/get/nasa`
50 |
51 | ```json
52 | {
53 | "url": "https://apod.nasa.gov/apod/image/1911/jC-L-TM_SunFinal5HRweb1024.jpg",
54 | "hdurl": "https://apod.nasa.gov/apod/image/1911/jC-L-TM_SunFinal5HRweb.jpg",
55 | "explanation": "On November 11, 2019 the Sun was mostly quiet, experiencing a minimum in its 11 year cycle of activity. In fact, the only spot v..."
56 | }
57 | ```
58 |
59 | Ready to go? head to the [Getting Started guide](https://isobeljs.com/guides-getting-started) to...well...get started!
60 |
61 | ## Supported services
62 |
63 | Isobel supports many services out of the box, here is the full list
64 |
65 | - [Dribbble](https://isobeljs.com/packages-services-dribbble-readme), retrieves your latest shots from Dribbble
66 | - [GitHub](https://isobeljs.com/packages-services-github-readme), retrives profile information from GitHub
67 | - [NASA](https://isobeljs.com/packages-services-nasa-readme), retrives the NASA photo of the day. Treat this one as an example for you to create your own services!
68 | - [Twitter](https://isobeljs.com/packages-services-twitter-readme), retrives your latest tweets!
69 | - [YouTube](https://isobeljs.com/packages-services-youtube-readme), retrives information about your YouTube Channel
70 |
71 | ## Supported caching strategies
72 |
73 | Isobel currently supports three caching strategies, with more on the way!
74 |
75 | - [Local file system](https://isobeljs.com/packages-file-system-readme)
76 | - [Dropbox](https://isobeljs.com/packages-dropbox-readme)
77 | - [Amazon S3 Buckets](https://isobeljs.com/packages-s3-readme)
78 |
79 | ## Acknowledgements
80 |
81 | Isobel is created by [Nathan Simpson](https://nathansimpson.design). Thank you to Thomas Walker for your inspiration, support and friendship.
82 |
--------------------------------------------------------------------------------