rm services/Instagram
This commit is contained in:
1 parent
4e13a18072
commit
56e21000a5
34 files changed
-1205
No files matched your search
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"presets": ["es2015", "react", "stage-0"]
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
.DS_Store
|
||||
.idea
|
||||
*.sublime*
|
||||
npm-debug.log
|
||||
|
||||
.private*
|
||||
|
||||
/node_modules
|
||||
/build
|
||||
/out
|
||||
/coverage
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Deploying Instagram Microservice"
|
||||
|
||||
#NODE_ENV=production gulp deploy
|
||||
|
||||
server="art.shburg.org"
|
||||
tarfile="build.tar.gz"
|
||||
tarpath=$TMPDIR${tarfile}
|
||||
destpath="~/www/art.shburg.org/services/instagram"
|
||||
private=".private"
|
||||
|
||||
echo "Building..."
|
||||
NODE_ENV=production npm run webpack
|
||||
npm run build
|
||||
|
||||
echo "Packing..."
|
||||
env GZIP=-9 tar -czf ${tarpath} \
|
||||
--disable-copyfile \
|
||||
--exclude=node_modules \
|
||||
--exclude=.git \
|
||||
--exclude=.idea \
|
||||
--exclude=.DS_Store \
|
||||
--exclude=.private* \
|
||||
--exclude=src \
|
||||
--exclude=deploy.sh \
|
||||
--exclude=install.sh \
|
||||
.
|
||||
|
||||
echo "Build file: $tarpath"
|
||||
|
||||
scp ${tarpath} hoster@${server}:${destpath}
|
||||
echo "Build file successfully uploaded on ${server}"
|
||||
|
||||
scp ${private} hoster@${server}:${destpath}
|
||||
|
||||
echo "Installing..."
|
||||
ssh hoster@${server} "bash -s" -- < ./install.sh ${destpath} ${tarfile}
|
||||
|
||||
rm ${tarpath}
|
||||
echo "Done"
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"uid": "hudozka-instagram",
|
||||
"append": true,
|
||||
"watch": false,
|
||||
"script": "index.js",
|
||||
"sourceDir": "/home/hoster/www/art.shburg.org/services/instagram/out",
|
||||
"wordingDir": "/home/hoster/www/art.shburg.org/services/instagram"
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
dir_path="$1"
|
||||
build_file="$2"
|
||||
app="hudozka-instagram"
|
||||
script="forever.json"
|
||||
|
||||
cd ${dir_path}
|
||||
gunzip -c ${build_file} | tar xopf -
|
||||
npm install --production --loglevel error
|
||||
|
||||
if forever restart ${app}; then
|
||||
echo Instagram Microservice restarted
|
||||
else
|
||||
forever start ${script}
|
||||
echo Instagram Microservice started
|
||||
fi
|
||||
@@ -1,39 +0,0 @@
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name art.shburg.org;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/art.shlisselburg.org/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/art.shlisselburg.org/privkey.pem;
|
||||
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_ciphers AES256+EECDH:AES256+EDH:!aNULL;
|
||||
|
||||
location /service/instagram {
|
||||
proxy_pass http://hudozka_instagram/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
}
|
||||
|
||||
error_log /var/log/nginx/art.shburg.instagram.error.log;
|
||||
access_log /var/log/nginx/art.shburg.instagram.access.log;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name art.shburg.org;
|
||||
return 301 https://art.shburg.org$request_uri;
|
||||
}
|
||||
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
upstream hudozka_instagram {
|
||||
server localhost:18000;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
{
|
||||
"name": "hudozka-instagram",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"devwebpack": "NODE_ENV=development webpack",
|
||||
"webpack": "webpack",
|
||||
"start": "babel-node src/index.js",
|
||||
"build": "babel src -d out"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.7.2",
|
||||
"co": "^4.6.0",
|
||||
"ejson": "^2.1.2",
|
||||
"instagram-node": "^0.5.8",
|
||||
"jsonwebtoken": "^5.7.0",
|
||||
"kerberos": "0.0.19",
|
||||
"koa": "^2.0.0-alpha.3",
|
||||
"koa-bodyparser": "^3.0.0",
|
||||
"koa-compose": "^3.1.0",
|
||||
"koa-convert": "^1.2.0",
|
||||
"koa-logger": "^2.0.0",
|
||||
"koa-mount": "^2.0.0",
|
||||
"koa-route": "^3.0.0",
|
||||
"koa-session-mongo": "^1.1.0",
|
||||
"koa-session-store": "^1.1.1",
|
||||
"koa-static": "^3.0.0",
|
||||
"mongodb": "^2.1.10",
|
||||
"mz": "^2.3.1",
|
||||
"react": "^0.14.7",
|
||||
"react-dom": "^0.14.7",
|
||||
"react-router": "^2.0.1",
|
||||
"request-promise": "^2.0.1",
|
||||
"thunkify": "^2.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.7.2",
|
||||
"babel-preset-es2015": "^6.6.0",
|
||||
"babel-preset-stage-0": "^6.5.0",
|
||||
"babel-cli": "^6.6.5",
|
||||
"babel-loader": "^6.2.4",
|
||||
"babel-preset-react": "^6.5.0",
|
||||
"html-webpack-plugin": "^2.10.0",
|
||||
"webpack": "^1.12.14"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export default class EndPoint{
|
||||
constructor(request) {
|
||||
this.request = request;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import EndPoint from './EndPoint';
|
||||
|
||||
export default class Feed extends EndPoint {
|
||||
add(post) {
|
||||
return this.request.request('PUT', '/timeline', post);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import {stringify} from 'ejson';
|
||||
import {EventEmitter} from 'events';
|
||||
import request from 'request-promise';
|
||||
import Feed from './Feed';
|
||||
|
||||
let json = i => stringify(i);
|
||||
|
||||
export default class Service extends EventEmitter{
|
||||
constructor(host, service, secret) {
|
||||
super();
|
||||
this.version = 'v1';
|
||||
this.host = host;
|
||||
this.service = service;
|
||||
this.secret = secret;
|
||||
this.feed = new Feed(this);
|
||||
}
|
||||
|
||||
request(method, path, data) {
|
||||
let buildPath = path => `${this.host}/api/${this.version}${path}`;
|
||||
let options = {
|
||||
method: method,
|
||||
uri: buildPath(path),
|
||||
headers: {
|
||||
'Content-Type': 'application/ejson',
|
||||
'X-Service-Name': this.service,
|
||||
'X-Service-Token': this.secret
|
||||
},
|
||||
body: json(data)
|
||||
};
|
||||
|
||||
return request(options);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import React from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import {Router, Route, browserHistory} from 'react-router';
|
||||
import App from './components/App';
|
||||
|
||||
render(
|
||||
(
|
||||
<Router history={browserHistory}>
|
||||
<Route path="/" component={App}>
|
||||
|
||||
</Route>
|
||||
</Router>
|
||||
),
|
||||
document.querySelector('.app-container')
|
||||
);
|
||||
|
||||
// <Route path="login" component={Login}/>
|
||||
// <Route path="logout" component={Logout}/>
|
||||
// <Route path="about" component={About}/>
|
||||
// <Route path="dashboard" component={Dashboard} onEnter={requireAuth}/>
|
||||
|
||||
// render(
|
||||
// <App/>,
|
||||
// document.querySelector('.app-container')
|
||||
// );
|
||||
@@ -1,18 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
export default class App extends React.Component{
|
||||
handleClick(event){
|
||||
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<div>
|
||||
<h1>Панель управления инстаграмом художки</h1>
|
||||
<p>Чтобы управлять, вы должны быть <a href="https://instagram.com/hudozka">hudozka</a></p>
|
||||
|
||||
<a href="auth" onClick={this.handleClick.bind(this)}>Войти</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import path from 'path';
|
||||
import {readFileSync} from 'fs';
|
||||
|
||||
const env = process.env['NODE_ENV'] || 'production';
|
||||
const isProduction = env === 'production';
|
||||
|
||||
let privatePath = path.join(
|
||||
__dirname,
|
||||
process.env['PRIVATE'] || '../.private'
|
||||
);
|
||||
let privateData = JSON.parse(
|
||||
readFileSync(privatePath, 'utf-8')
|
||||
);
|
||||
|
||||
export const staticPath = path.join(
|
||||
__dirname,
|
||||
'../build'
|
||||
);
|
||||
|
||||
export const port = isProduction ? 18000 : 3000;
|
||||
export const pathPrefix = isProduction ? '/service/instagram' : '';
|
||||
export const baseHref = isProduction ? '/service/instagram/' : '/';
|
||||
// export const pathPrefix = '';
|
||||
export const indexPath = path.join(
|
||||
__dirname,
|
||||
// './templates/index.html'
|
||||
'../build/main.html'
|
||||
);
|
||||
// export const indexPath = '../build/main.html';
|
||||
|
||||
export const clientId = privateData['clientId'];
|
||||
export const clientSecret = privateData['clientSecret'];
|
||||
export const redirectUri = privateData['redirectUri'];
|
||||
|
||||
export const serviceHost = isProduction ? 'http://localhost:1800' : 'http://localhost:8080';
|
||||
export const serviceName = 'instagram';
|
||||
export const serviceSecret = privateData['serviceSecret'];
|
||||
|
||||
export const jwtSecret = privateData['jwtSecret'];
|
||||
|
||||
export const sessionSecret = privateData['sessionSecret'];
|
||||
export const sessionExpirationTime = 60 * 60 * 24 * 2; // two days in seconds
|
||||
|
||||
export const authScope = [
|
||||
'basic',
|
||||
// 'public_content',
|
||||
// 'follower_list',
|
||||
'comments',
|
||||
// 'relationships',
|
||||
'likes'
|
||||
];
|
||||
|
||||
export const postType = 'instagram-2';
|
||||
export const fetchItemsPerUpdateLoop = 1;
|
||||
export const updateSubscriptionDelay = 1000 * 60 * 60; // one hour in milliseconds
|
||||
export const updateSubscriptionCallbackUrl = isProduction ? `https://art.shburg.org${pathPrefix}/callback/hudozka` : 'http://dev.tmshv.ru/callback/hudozka';
|
||||
|
||||
export const database = {
|
||||
host: 'localhost',
|
||||
port: 27017,
|
||||
db: 'hudozka',
|
||||
username: privateData['databaseUser'],
|
||||
password: privateData['databasePassword']
|
||||
};
|
||||
|
||||
const databaseAuth = database.username && database.password ? `${database.username}:${database.password}@` : '';
|
||||
export const databaseUri = `mongodb://${databaseAuth}${database.host}:${database.port}/${database.db}`;
|
||||
@@ -1,48 +0,0 @@
|
||||
import {collection} from './db';
|
||||
import {jwtSecret} from '../config';
|
||||
import {sign} from 'jsonwebtoken';
|
||||
|
||||
/**
|
||||
* {
|
||||
* username: String,
|
||||
* bio: String,
|
||||
* website: String,
|
||||
* profile_picture: String,
|
||||
* full_name: String,
|
||||
* id: String
|
||||
* }
|
||||
*
|
||||
* @param accessToken
|
||||
* @param user
|
||||
*/
|
||||
export async function authorizeUser(accessToken, user) {
|
||||
const provider = 'instagram';
|
||||
const users = collection('users');
|
||||
|
||||
let profile = await users.findOne({
|
||||
provider: provider,
|
||||
username: user.username
|
||||
});
|
||||
|
||||
if(profile){
|
||||
Object.keys(user).forEach(i => {
|
||||
profile[i] = user[i];
|
||||
});
|
||||
profile.accessToken = accessToken;
|
||||
profile.jwtToken = await createToken(profile);
|
||||
|
||||
await users.update({_id: profile['_id']}, profile);
|
||||
return profile;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function createToken(user){
|
||||
return new Promise((resolve, reject) => {
|
||||
sign(user, jwtSecret, {}, token => {
|
||||
if (!token) return reject();
|
||||
resolve(token);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import {MongoClient} from 'mongodb';
|
||||
import co from 'co';
|
||||
|
||||
export var db;
|
||||
|
||||
export function connect(uri){
|
||||
return co(function*() {
|
||||
db = yield MongoClient.connect(uri);
|
||||
return db;
|
||||
})
|
||||
}
|
||||
|
||||
export function collection(name){
|
||||
return db.collection(name);
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
import {collection} from './db';
|
||||
import {api} from '../instagram';
|
||||
import {postFromInstagramMedia} from '../models/post';
|
||||
import Service from '../api/Service';
|
||||
import {serviceHost, serviceName, serviceSecret, fetchItemsPerUpdateLoop as num} from '../config';
|
||||
|
||||
const service = new Service(serviceHost, serviceName, serviceSecret);
|
||||
|
||||
var instagramClient;
|
||||
|
||||
export default async function update(id, data) {
|
||||
const updateTimestamp = data.time;
|
||||
let items = await fetch(updateTimestamp);
|
||||
return await Promise.all(
|
||||
items.map(post => push(post))
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetch(updateTimestamp) {
|
||||
const instagram = await client();
|
||||
try {
|
||||
let data = await instagram.user.mediaRecent(null, 1, updateTimestamp);
|
||||
return data.medias.map(postFromInstagramMedia);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function push(data) {
|
||||
try {
|
||||
return await service.feed.add(data);
|
||||
} catch (e) {
|
||||
console.log('PUSHING ERROR');
|
||||
console.log(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function clearSubscriptions(callbackUrl) {
|
||||
const instagram = await client();
|
||||
return await instagram.subscriptions.removeAll();
|
||||
}
|
||||
|
||||
export async function updateSubscription(callbackUrl) {
|
||||
const instagram = await client();
|
||||
|
||||
let data = await instagram.subscriptions.list();
|
||||
let activeSubscriptions = data.result;
|
||||
|
||||
console.log(`Active subscriptions ${activeSubscriptions}`);
|
||||
|
||||
let objectSubscriptions = activeSubscriptions.filter(i => i.object === 'user' && !i.object_id && i.callback_url === callbackUrl)
|
||||
|
||||
if (!objectSubscriptions.length) {
|
||||
// {
|
||||
// result: {
|
||||
// object: 'tag',
|
||||
// object_id: 'hello_lol',
|
||||
// aspect: 'media',
|
||||
// callback_url: 'http://dev.tmshv.ru/callback/hudozka',
|
||||
// type: 'subscription',
|
||||
// id: '22502218'
|
||||
// },
|
||||
// remaining: 4988,
|
||||
// limit: 5000
|
||||
// }
|
||||
|
||||
// {
|
||||
// result: {
|
||||
// object: 'user',
|
||||
// object_id: null,
|
||||
// aspect: 'media',
|
||||
// callback_url: 'http://dev.tmshv.ru/callback/hudozka',
|
||||
// type: 'subscription',
|
||||
// id: '22501989'
|
||||
// },
|
||||
// remaining: 4995,
|
||||
// limit: 5000
|
||||
// }
|
||||
await instagram.subscriptions.subscribeUser(callbackUrl);
|
||||
}
|
||||
}
|
||||
|
||||
async function client() {
|
||||
if (instagramClient) return instagramClient;
|
||||
|
||||
const provider = 'instagram';
|
||||
const users = collection('users');
|
||||
|
||||
let profile = await users.findOne({
|
||||
provider: provider
|
||||
});
|
||||
|
||||
if (profile) {
|
||||
instagramClient = api(profile.accessToken);
|
||||
return instagramClient;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import 'babel-polyfill';
|
||||
|
||||
import {databaseUri, port, updateSubscriptionDelay, updateSubscriptionCallbackUrl} from './config'
|
||||
import {connect} from './core/db';
|
||||
import server from './server';
|
||||
// import update from './core/update';
|
||||
import {updateSubscription, clearSubscriptions} from './core/update';
|
||||
|
||||
let repeat = time => fn => setInterval(fn, time);
|
||||
|
||||
function main(){
|
||||
async function loop(){
|
||||
try{
|
||||
await connect(databaseUri);
|
||||
}catch(error){
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
try{
|
||||
let app = server();
|
||||
app.listen(port);
|
||||
console.log(`Start listening ${port}`);
|
||||
}catch(error){
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
await clearSubscriptions();
|
||||
await updateSubscription(updateSubscriptionCallbackUrl);
|
||||
repeat(updateSubscriptionDelay)(() => {
|
||||
updateSubscription(updateSubscriptionCallbackUrl);
|
||||
});
|
||||
}
|
||||
|
||||
loop();
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
// .then(() => {
|
||||
// return update('hudozka', {
|
||||
// "changed_aspect": "media",
|
||||
// "object": "tag",
|
||||
// "object_id": "love",
|
||||
// // "time": 1458675620,
|
||||
// "time": 1458594000,
|
||||
// "subscription_id": 22487204,
|
||||
// "data": {}
|
||||
// })
|
||||
// .catch(e => {
|
||||
// console.log(e);
|
||||
// })
|
||||
// .then(result => {
|
||||
// console.log('update done');
|
||||
// console.log(result);
|
||||
// });
|
||||
// })
|
||||
@@ -1,25 +0,0 @@
|
||||
import request from 'request-promise';
|
||||
|
||||
export default class EndPoint{
|
||||
constructor(client, token) {
|
||||
this.client = client;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
method(name){
|
||||
return this.client[name];
|
||||
}
|
||||
|
||||
request(method, path, data=null) {
|
||||
let buildPath = path => `https://api.instagram.com/v1${path}`;
|
||||
let options = {
|
||||
method: method,
|
||||
uri: buildPath(path),
|
||||
headers: {}
|
||||
};
|
||||
|
||||
if(data) options.body = data;
|
||||
return request(options)
|
||||
.then(i => JSON.parse(i));
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import Subscription from './Subscription';
|
||||
import User from './User';
|
||||
import Media from './Media';
|
||||
|
||||
export default class Instagram {
|
||||
constructor(clientId, clientSecret, accessToken, createApi) {
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
this.accessToken = accessToken;
|
||||
|
||||
const authenticatedApi = createApi(clientId, clientSecret, accessToken);
|
||||
const api = createApi(clientId, clientSecret);
|
||||
|
||||
//noinspection JSUnresolvedVariable
|
||||
this.user = new User(authenticatedApi);
|
||||
this.subscriptions = new Subscription(api);
|
||||
this.media = new Media(api, accessToken);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import EndPoint from './EndPoint';
|
||||
|
||||
export default class Media extends EndPoint {
|
||||
shortname(name){
|
||||
return this.request('GET', `/media/shortcode/${name}?access_token=${this.token}`);
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import EndPoint from './EndPoint';
|
||||
|
||||
export default class Subscription extends EndPoint {
|
||||
list() {
|
||||
const m = this.method('subscriptions');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
m((error, result, remaining, limit) => {
|
||||
if(error) return reject(error);
|
||||
resolve({
|
||||
result: result,
|
||||
remaining: remaining,
|
||||
limit: limit
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
subscribeTag(tag, callbackUrl) {
|
||||
const m = this.method('add_tag_subscription');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
m(tag, callbackUrl, (error, result, remaining, limit) => {
|
||||
if(error) return reject(error);
|
||||
resolve({
|
||||
result: result,
|
||||
remaining: remaining,
|
||||
limit: limit
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
subscribeUser(callbackUrl) {
|
||||
const m = this.method('add_user_subscription');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
m(callbackUrl, (error, result, remaining, limit) => {
|
||||
if(error) return reject(error);
|
||||
resolve({
|
||||
result: result,
|
||||
remaining: remaining,
|
||||
limit: limit
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
removeAll(){
|
||||
const m = this.method('del_subscription');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
m({all: true}, (error, result, remaining, limit) => {
|
||||
if (error) return reject(error);
|
||||
resolve({
|
||||
result: result,
|
||||
remaining: remaining,
|
||||
limit: limit
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
remove(id){
|
||||
// ig.del_subscription({ id: 1 }, function(err, subscriptions, remaining, limit){});
|
||||
}
|
||||
|
||||
// unsubscribe: function *(id) {
|
||||
// var ig = createClient();
|
||||
// var sub = thunkify(ig["del_subscription"]);
|
||||
// try {
|
||||
// var s = yield sub({id: id});
|
||||
// return s[0];
|
||||
// } catch (error) {
|
||||
// return null;
|
||||
// }
|
||||
// },
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import EndPoint from './EndPoint';
|
||||
|
||||
export default class User extends EndPoint {
|
||||
mediaRecent(id = null, count = null, minTime = null, maxTime = null) {
|
||||
function callback(resolve, reject) {
|
||||
return function (error, medias, pagination, remaining, limit) {
|
||||
if (error) return reject(error);
|
||||
resolve({
|
||||
medias: medias,
|
||||
pagination: pagination,
|
||||
remaining: remaining,
|
||||
limit: limit
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const options = {};
|
||||
if (count) options.count = count;
|
||||
if (minTime) options.min_timestamp = minTime;
|
||||
if (maxTime) options.max_timestamp = maxTime;
|
||||
|
||||
if (id) {
|
||||
let m = this.method('user_media_recent');
|
||||
return new Promise((resolve, reject) => {
|
||||
m(id, options, callback(resolve, reject));
|
||||
});
|
||||
} else {
|
||||
let m = this.method('user_self_media_recent');
|
||||
return new Promise((resolve, reject) => {
|
||||
m(options, callback(resolve, reject));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import {instagram} from 'instagram-node';
|
||||
|
||||
import {clientId, clientSecret} from '../config';
|
||||
import Instagram from './Instagram';
|
||||
|
||||
export function api(accessToken){
|
||||
return new Instagram(clientId, clientSecret, accessToken, createClient);
|
||||
}
|
||||
|
||||
export function createAuthorizationUrl(redirectUri, scope, state) {
|
||||
let params = {
|
||||
scope: scope || ['basic']
|
||||
};
|
||||
if (state) params.state = state;
|
||||
|
||||
let client = createClient(clientId, clientSecret);
|
||||
return client['get_authorization_url'](redirectUri, params);
|
||||
}
|
||||
|
||||
export function authorize(code, redirectUri) {
|
||||
let api = createClient(clientId, clientSecret);
|
||||
let authorize = api['authorize_user'];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
authorize(code, redirectUri, (error, user) => {
|
||||
if(error){
|
||||
reject(error);
|
||||
}else{
|
||||
resolve(user);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createClient(clientId, clientSecret, accessToken = null) {
|
||||
const client = instagram();
|
||||
client.use({
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret
|
||||
});
|
||||
if (accessToken) client.use({access_token: accessToken});
|
||||
return client;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import {api} from '../instagram';
|
||||
|
||||
export default function () {
|
||||
return async(ctx, next) => {
|
||||
var code = ctx.session.accessToken;
|
||||
if (code) {
|
||||
ctx.instagram = api(code);
|
||||
}
|
||||
|
||||
await next();
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import {postType} from '../config';
|
||||
|
||||
let date = i => new Date(parseInt(i) * 1000);
|
||||
|
||||
/**
|
||||
* @param i
|
||||
* {
|
||||
* "attribution": null,
|
||||
* "tags": ["люстра", "кукольныйдомик", "shlb_hudozka", "шлиссельбург", "стекло", "выставочноепространство", "художка"],
|
||||
* "type": "image",
|
||||
* "location": null,
|
||||
* "comments": {"count": 0, "data": []},
|
||||
* "filter": "Normal",
|
||||
* "created_time": "1458652687",
|
||||
* "link": "https://www.instagram.com/p/BDQbcm4Jiid/",
|
||||
* "likes": {
|
||||
* "count": 138,
|
||||
* "data": [{
|
||||
* "username": "marinada29",
|
||||
* "profile_picture": "https://scontent.cdninstagram.com/t51.2885-19/10958645_950152745008546_618790272_a.jpg",
|
||||
* "id": "326555798",
|
||||
* "full_name": "Marina Davidovich 89024808351"
|
||||
* }]
|
||||
* },
|
||||
* "images": {
|
||||
* "low_resolution": {
|
||||
* "url": "https://scontent.cdninstagram.com/t51.2885-15/s320x320/e35/12479404_949349891830020_632926748_n.jpg?ig_cache_key=MTIxMTU4OTAxMjkwODAyNjAxMw%3D%3D.2.l",
|
||||
* "width": 320,
|
||||
* "height": 320
|
||||
* },
|
||||
* "thumbnail": {
|
||||
* "url": "https://scontent.cdninstagram.com/t51.2885-15/s150x150/e35/c0.134.1080.1080/12816835_959212777507222_1085128603_n.jpg?ig_cache_key=MTIxMTU4OTAxMjkwODAyNjAxMw%3D%3D.2.c",
|
||||
* "width": 150,
|
||||
* "height": 150
|
||||
* },
|
||||
* "standard_resolution": {
|
||||
* "url": "https://scontent.cdninstagram.com/t51.2885-15/s640x640/sh0.08/e35/12479404_949349891830020_632926748_n.jpg?ig_cache_key=MTIxMTU4OTAxMjkwODAyNjAxMw%3D%3D.2.l",
|
||||
* "width": 640,
|
||||
* "height": 640
|
||||
* }
|
||||
* },
|
||||
* "users_in_photo": [],
|
||||
* "caption": {
|
||||
* "created_time": "1458652687",
|
||||
* "text": "Люстра. В выставочном пространстве \"Кукольный домик\" оформляется одна из ниш. #shlb_hudozka #кукольныйдомик #выставочноепространство #стекло #люстра #художка #шлиссельбург",
|
||||
* "from": {
|
||||
* "username": "hudozka",
|
||||
* "profile_picture": "https://scontent.cdninstagram.com/t51.2885-19/11357496_464997000344800_2124591831_a.jpg",
|
||||
* "id": "1668537441",
|
||||
* "full_name": "Шлиссельбургская ДХШ"
|
||||
* },
|
||||
* "id": "1211589015567214752"
|
||||
* },
|
||||
* "user_has_liked": false,
|
||||
* "id": "1211589012908026013_1668537441",
|
||||
* "user": {
|
||||
* "username": "hudozka",
|
||||
* "profile_picture": "https://scontent.cdninstagram.com/t51.2885-19/11357496_464997000344800_2124591831_a.jpg",
|
||||
* "id": "1668537441",
|
||||
* "full_name": "Шлиссельбургская ДХШ"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export const postFromInstagramMedia = i => ({
|
||||
data: i,
|
||||
type: postType,
|
||||
date: date(i['created_time']),
|
||||
id: i.id
|
||||
});
|
||||
@@ -1,64 +0,0 @@
|
||||
import {get, url} from './';
|
||||
import {authorize, createAuthorizationUrl} from '../instagram';
|
||||
import {redirectUri, authScope} from '../config';
|
||||
import {authorizeUser} from '../core/auth';
|
||||
|
||||
export function auth() {
|
||||
return get('/auth', async(ctx) => {
|
||||
const state = url('/');
|
||||
const i = createAuthorizationUrl(redirectUri, authScope, state);
|
||||
ctx.redirect(i, state);
|
||||
});
|
||||
}
|
||||
|
||||
export function authCallback() {
|
||||
const root = url('/');
|
||||
|
||||
return get('/auth/callback', async(ctx) => {
|
||||
let state = ctx.query.state;
|
||||
let code = ctx.query.code;
|
||||
|
||||
if(!ctx.session.accessToken) {
|
||||
try{
|
||||
var client = await authorize(code, redirectUri);
|
||||
}catch (e){
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
if (client){
|
||||
var user = await authorizeUser(client.access_token, client.user);
|
||||
var accessToken = client.access_token;
|
||||
}
|
||||
|
||||
if(user) ctx.cookies.set('jwt', user.jwtToken);
|
||||
ctx.status = user ? 200 : 401;
|
||||
ctx.session.user = user;
|
||||
ctx.session.accessToken = accessToken;
|
||||
}
|
||||
|
||||
ctx.redirect(state, root);
|
||||
});
|
||||
}
|
||||
|
||||
export function authOut(redirectUrl = '/') {
|
||||
const redirectTo = url(redirectUrl);
|
||||
const root = url('/');
|
||||
|
||||
return get('/auth/out', async(ctx) => {
|
||||
ctx.session = null;
|
||||
ctx.redirect(redirectTo, root);
|
||||
});
|
||||
}
|
||||
|
||||
export function ensureAuth(failRedirect = '/') {
|
||||
const redirectTo = url(failRedirect);
|
||||
const root = url('/');
|
||||
|
||||
return async (ctx, next) => {
|
||||
if(!ctx.session.user) {
|
||||
ctx.redirect(redirectTo, root)
|
||||
}else{
|
||||
await next();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import {get, post} from './';
|
||||
import update from '../core/update';
|
||||
|
||||
export function callbackGet() {
|
||||
return get('/callback/:id', async (ctx, id) => {
|
||||
// console.log('challenge', id);
|
||||
|
||||
if(ctx.query['hub.mode'] == 'subscribe'){
|
||||
ctx.body = ctx.query['hub.challenge'];
|
||||
}else{
|
||||
ctx.status = 400;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function callbackPost() {
|
||||
return post('/callback/:id', async (ctx, id) => {
|
||||
let body = ctx.request.body;
|
||||
// console.log(id, body);
|
||||
update(id, body);
|
||||
|
||||
ctx.status = 200;
|
||||
ctx.body = '';
|
||||
|
||||
//body example
|
||||
// {
|
||||
// "changed_aspect": "media",
|
||||
// "object": "tag",
|
||||
// "object_id": "love",
|
||||
// "time": 1458675620,
|
||||
// "subscription_id": 22487204,
|
||||
// "data": {}
|
||||
// }
|
||||
|
||||
// let sign = ctx.header['x-hub-signature'];
|
||||
//var hmac = crypto.createHmac("sha1", instagram.client_secret);
|
||||
//hmac.update(body);
|
||||
//var shasum = hmac.digest("hex");
|
||||
|
||||
//console.log("X-Hub-Signature", sign);
|
||||
//console.log("Hmac", shasum);
|
||||
//console.log();
|
||||
|
||||
//if(shasum.digest("hex") == sign) {
|
||||
// instagram.update(body);
|
||||
//}
|
||||
});
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import fs from 'mz/fs';
|
||||
import compose from 'koa-compose';
|
||||
import route from 'koa-route';
|
||||
import {indexPath, pathPrefix} from '../config';
|
||||
|
||||
import main from './main';
|
||||
import {auth, authCallback, authOut} from './auth';
|
||||
import {list, addTag, addUser} from './subscription';
|
||||
import {ensureAuth} from './auth';
|
||||
import {callbackGet, callbackPost} from './callback';
|
||||
import {shortname} from './media';
|
||||
|
||||
export function statelessRouters() {
|
||||
return compose([
|
||||
callbackGet(),
|
||||
callbackPost()
|
||||
]);
|
||||
}
|
||||
|
||||
export function routers() {
|
||||
return compose([
|
||||
// main(),
|
||||
auth(),
|
||||
authCallback(),
|
||||
authOut(),
|
||||
user(),
|
||||
list(),
|
||||
addTag(),
|
||||
addUser(),
|
||||
shortname()
|
||||
]);
|
||||
}
|
||||
|
||||
export const url = (path, root=pathPrefix) => `${root}${path}`;
|
||||
// export const url = path => path;
|
||||
export const get = (path, fn) => route['get'](url(path, ''), fn);
|
||||
export const post = (path, fn) => route['post'](url(path, ''), fn);
|
||||
|
||||
export function index(fn){
|
||||
return async (ctx) => {
|
||||
try{
|
||||
ctx.type = 'text/html';
|
||||
ctx.body = await fs.createReadStream(indexPath);
|
||||
}catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function user(){
|
||||
return compose([
|
||||
ensureAuth(),
|
||||
get('/user', async (ctx) => {
|
||||
const user = ctx.session.user.id;
|
||||
const instagram = ctx.instagram;
|
||||
|
||||
let data = await instagram.user.mediaRecent(user);
|
||||
|
||||
console.log(data);
|
||||
ctx.body = data;
|
||||
})
|
||||
]);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import {index, get} from './';
|
||||
|
||||
export default function () {
|
||||
return get('/', async (ctx) => {
|
||||
await index()(ctx);
|
||||
});
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import compose from 'koa-compose';
|
||||
import {get} from './';
|
||||
import {ensureAuth} from './auth';
|
||||
import {push} from '../core/update';
|
||||
import {postFromInstagramMedia} from '../models/post';
|
||||
|
||||
export function shortname() {
|
||||
return compose([
|
||||
ensureAuth(),
|
||||
get('/media/shortname/:name', async(ctx, name) => {
|
||||
const instagram = ctx.instagram;
|
||||
let result = await instagram.media.shortname(name);
|
||||
|
||||
if(result.meta.code === 200) {
|
||||
let post = postFromInstagramMedia(result.data);
|
||||
console.log('SHORTNAME');
|
||||
|
||||
ctx.set('Content-Type', 'application/json');
|
||||
ctx.body = await push(post);
|
||||
}
|
||||
})
|
||||
]);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import compose from 'koa-compose';
|
||||
import {get} from './';
|
||||
import {ensureAuth} from './auth';
|
||||
|
||||
export function list() {
|
||||
return compose([
|
||||
ensureAuth(),
|
||||
get('/subscriptions', async(ctx) => {
|
||||
const instagram = ctx.instagram;
|
||||
|
||||
ctx.set('Content-Type', 'application/json');
|
||||
ctx.body = await instagram.subscriptions.list();
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
export function addTag() {
|
||||
return compose([
|
||||
ensureAuth(),
|
||||
get('/subscriptions/add-tag/:tag', async(ctx, tag) => {
|
||||
let callbackUrl = ctx.query.url;
|
||||
let result = await ctx.instagram.subscriptions.subscribeTag(tag, callbackUrl);
|
||||
if (result) {
|
||||
ctx.body = result;
|
||||
} else {
|
||||
ctx.status = 500;
|
||||
}
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
export function addUser() {
|
||||
return compose([
|
||||
ensureAuth(),
|
||||
get('/subscriptions/add-user', async (ctx) => {
|
||||
let callbackUrl = ctx.query.url;
|
||||
let result = await ctx.instagram.subscriptions.subscribeUser(callbackUrl);
|
||||
if (result) {
|
||||
ctx.body = result;
|
||||
} else {
|
||||
ctx.status = 500;
|
||||
}
|
||||
})
|
||||
])
|
||||
}
|
||||
|
||||
// export function remove() {
|
||||
// return get('/subscriptions/remove/:id', async (ctx, id) => {
|
||||
// let result = yield instagram.api.unsubscribe(id);
|
||||
// if(result) {
|
||||
// ctx.body = result;
|
||||
// }else{
|
||||
// ctx.status = 500;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
@@ -1,53 +0,0 @@
|
||||
import Koa from 'koa';
|
||||
import mount from 'koa-mount';
|
||||
import serve from 'koa-static';
|
||||
import logger from 'koa-logger';
|
||||
import convert from 'koa-convert';
|
||||
import sessionStore from 'koa-session-store';
|
||||
import store from 'koa-session-mongo';
|
||||
import bodyParser from 'koa-bodyparser';
|
||||
|
||||
import {db} from './core/db';
|
||||
import instagram from './middlewares/instagram';
|
||||
import {routers, statelessRouters} from './routers';
|
||||
import {pathPrefix, staticPath, sessionSecret, sessionExpirationTime} from './config';
|
||||
|
||||
export default function(){
|
||||
const app = new Koa();
|
||||
app.proxy = true;
|
||||
app.keys = [sessionSecret];
|
||||
|
||||
app.use(logger());
|
||||
app.use(serve(staticPath));
|
||||
app.use(bodyParser());
|
||||
app.use(statelessRouters());
|
||||
app.use(session('sid', sessionExpirationTime));
|
||||
app.use(instagram());
|
||||
app.use(routers());
|
||||
app.use(error());
|
||||
|
||||
return app;
|
||||
|
||||
// const root = new Koa();
|
||||
// root.use(mount(pathPrefix, app));
|
||||
|
||||
// return root;
|
||||
}
|
||||
|
||||
function session(name, exp){
|
||||
return convert(sessionStore({
|
||||
name: name,
|
||||
expirationTime: exp,
|
||||
store: store.create({
|
||||
db: db,
|
||||
collection: 'sessions'
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
function error(){
|
||||
return ctx => {
|
||||
ctx.status = 404;
|
||||
ctx.body = `Document ${ctx.path} not found`;
|
||||
};
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Instagram Control</title>
|
||||
<base href="<%= htmlWebpackPlugin.options.baseHref %>">
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,54 +0,0 @@
|
||||
import webpack from'webpack';
|
||||
import HTML from 'html-webpack-plugin';
|
||||
|
||||
const env = process.env['NODE_ENV'] || 'production';
|
||||
const isProduction = env === 'production';
|
||||
|
||||
// import autoprefixer from 'autoprefixer';
|
||||
|
||||
import {baseHref} from './src/config';
|
||||
|
||||
module.exports = {
|
||||
entry: './src/app.js',
|
||||
watch: !isProduction,
|
||||
//resolve: {
|
||||
// modulesDirectories: [
|
||||
// './node_modules'
|
||||
// ]
|
||||
//},
|
||||
output: {
|
||||
path: './build',
|
||||
filename: 'app.js'
|
||||
},
|
||||
// devServer: {
|
||||
// historyApiFallback: true,
|
||||
// hot: true,
|
||||
// inline: true,
|
||||
// progress: true,
|
||||
// host: 'localhost',
|
||||
// port: 3000
|
||||
// },
|
||||
plugins: [
|
||||
// new webpack.HotModuleReplacementPlugin(),
|
||||
new HTML({
|
||||
filename: 'index.html',
|
||||
template: 'src/templates/index.ejs',
|
||||
inject: 'body',
|
||||
baseHref: baseHref
|
||||
})
|
||||
],
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
// exclude: /node_modules/,
|
||||
//exclude: /(node_modules|bower_components)/,
|
||||
loader: 'babel',
|
||||
query: {
|
||||
// presets: ['react'],
|
||||
cacheDirectory: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
Reference in new issue
Block a user