├── .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 | 3 | 4 | v 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 🐶 12 | 13 | 14 | Your 15 | website/app 16 | 17 | 18 | Other 19 | services 20 | 21 | 22 | Custom 23 | services 24 | 25 | 26 | 27 | 28 | 29 | Cache 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /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 | ![Diagram](./guides/diagram.svg) 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 | --------------------------------------------------------------------------------