diff --git a/services/Instagram/package.json b/services/Instagram/package.json index 79a9ea89..6dfc3742 100644 --- a/services/Instagram/package.json +++ b/services/Instagram/package.json @@ -11,7 +11,9 @@ "babel-preset-es2015": "^6.6.0", "babel-preset-stage-0": "^6.5.0", "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", @@ -25,6 +27,7 @@ "mz": "^2.3.1", "react": "^0.14.7", "react-dom": "^0.14.7", + "react-router": "^2.0.1", "thunkify": "^2.1.2" }, "devDependencies": { diff --git a/services/Instagram/src/api/EndPoint.js b/services/Instagram/src/api/EndPoint.js index 4fb851be..bdf22d65 100644 --- a/services/Instagram/src/api/EndPoint.js +++ b/services/Instagram/src/api/EndPoint.js @@ -1,5 +1,5 @@ export default class EndPoint{ - constructor() { - + constructor(request) { + this.request = request; } } \ No newline at end of file diff --git a/services/Instagram/src/api/Feed.js b/services/Instagram/src/api/Feed.js index a9f274d9..63bdad55 100644 --- a/services/Instagram/src/api/Feed.js +++ b/services/Instagram/src/api/Feed.js @@ -2,8 +2,6 @@ import EndPoint from './EndPoint'; export default class Feed extends EndPoint { add(post) { - return new Promise((resolve, reject) => { - - }); + return this.request.request('PUT', '/feed', post); } } diff --git a/services/Instagram/src/api/Service.js b/services/Instagram/src/api/Service.js index 5075b686..b81dbfa4 100644 --- a/services/Instagram/src/api/Service.js +++ b/services/Instagram/src/api/Service.js @@ -1,15 +1,33 @@ +import {stringify} from 'ejson'; import {EventEmitter} from 'events'; +import request from 'request-promise'; import Feed from './Feed'; -export default class Service extends EventEmitter{ - constructor(secret) { - super(); +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); + this.feed = new Feed(this); } - request(){ - + 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); } } diff --git a/services/Instagram/src/app.js b/services/Instagram/src/app.js index 291bab09..9fe18f67 100644 --- a/services/Instagram/src/app.js +++ b/services/Instagram/src/app.js @@ -1,8 +1,25 @@ import React from 'react'; -import ReactDOM from 'react-dom'; +import {render} from 'react-dom'; +import {Router, Route, browserHistory} from 'react-router'; import App from './components/App'; -ReactDOM.render( - , - document.querySelector('.app-container') +render( + ( + + + + + + ), + document.querySelector('.app-container') ); + +// +// +// +// + +// render( +// , +// document.querySelector('.app-container') +// ); diff --git a/services/Instagram/src/components/App.js b/services/Instagram/src/components/App.js index 6cd69a56..88c472e6 100644 --- a/services/Instagram/src/components/App.js +++ b/services/Instagram/src/components/App.js @@ -1,7 +1,18 @@ import React from 'react'; export default class App extends React.Component{ + handleClick(event){ + + } + render(){ - return (

Hi

) + return ( +
+

Панель управления инстаграмом художки

+

Чтобы управлять, вы должны быть hudozka

+ + Войти +
+ ) } } diff --git a/services/Instagram/src/config.js b/services/Instagram/src/config.js index e0792dcd..6b83afdf 100644 --- a/services/Instagram/src/config.js +++ b/services/Instagram/src/config.js @@ -16,8 +16,15 @@ 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', @@ -27,9 +34,10 @@ export const authScope = [ 'likes' ]; -export const sessionSecret = privateData['sessionSecret']; -// export const sessionExpirationTime = 60 * 60 * 24 * 2; // two days in seconds -export const sessionExpirationTime = 10; // 10 seconds +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', @@ -40,4 +48,3 @@ export const database = { }; export const databaseUri = `mongodb://${database.host}:${database.port}/${database.db}`; - diff --git a/services/Instagram/src/core/auth.js b/services/Instagram/src/core/auth.js index 1fc23d9b..26b6a677 100644 --- a/services/Instagram/src/core/auth.js +++ b/services/Instagram/src/core/auth.js @@ -1,4 +1,6 @@ import {collection} from './db'; +import {jwtSecret} from '../config'; +import {sign} from 'jsonwebtoken'; /** * { @@ -27,6 +29,7 @@ export async function authorizeUser(accessToken, user) { profile[i] = user[i]; }); profile.accessToken = accessToken; + profile.jwtToken = await createToken(profile); await users.update({_id: profile['_id']}, profile); return profile; @@ -34,3 +37,12 @@ export async function authorizeUser(accessToken, user) { return null; } } + +async function createToken(user){ + return new Promise((resolve, reject) => { + sign(user, jwtSecret, {}, token => { + if (!token) return reject(); + resolve(token); + }); + }); +} diff --git a/services/Instagram/src/core/update.js b/services/Instagram/src/core/update.js index 177ed22e..1107d43c 100644 --- a/services/Instagram/src/core/update.js +++ b/services/Instagram/src/core/update.js @@ -2,9 +2,9 @@ import {collection} from './db'; import {api} from '../instagram'; import {postFromInstagramMedia} from '../models/post'; import Service from '../api/Service'; -import {serviceSecret} from '../config'; +import {serviceHost, serviceName, serviceSecret, fetchItemsPerUpdateLoop as num} from '../config'; -const service = new Service(serviceSecret); +const service = new Service(serviceHost, serviceName, serviceSecret); var instagramClient; @@ -15,14 +15,63 @@ export default async function update(id, data) { let data = await instagram.user.mediaRecent(null, 1, updateTimestamp); let items = data.medias.map(postFromInstagramMedia); - for (let post of items) { - await service.feed.add(post); - } + return await Promise.all( + items.map(post => service.feed.add(post)) + ); } catch (e) { console.log(e); } } +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(data); + + 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); + // let data = await instagram.subscriptions.subscribeTag('love', callbackUrl); + console.log(data); + + return data; + } +} + async function client() { if (instagramClient) return instagramClient; diff --git a/services/Instagram/src/instagram/Subscription.js b/services/Instagram/src/instagram/Subscription.js index 30ab2bf1..cba0fc9b 100644 --- a/services/Instagram/src/instagram/Subscription.js +++ b/services/Instagram/src/instagram/Subscription.js @@ -47,7 +47,18 @@ export default class Subscription extends EndPoint { } removeAll(){ - // ig.del_subscription({ all: true }, function(err, subscriptions, remaining, limit){}); + 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){ diff --git a/services/Instagram/src/main.js b/services/Instagram/src/main.js index 83cad99a..433f3c55 100644 --- a/services/Instagram/src/main.js +++ b/services/Instagram/src/main.js @@ -1,7 +1,8 @@ -import {databaseUri, port} from './config' +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'; connect(databaseUri) .catch(error => { @@ -14,14 +15,30 @@ connect(databaseUri) .then(() => { console.log(`Start listening ${port}`); }) + .then(clearSubscriptions) .then(() => { - return update('hudozka', { - "changed_aspect": "media", - "object": "tag", - "object_id": "love", - // "time": 1458675620, - "time": 1458594000, - "subscription_id": 22487204, - "data": {} - }) + let repeat = time => fn => setInterval(fn, time); + + updateSubscription(updateSubscriptionCallbackUrl); + repeat(updateSubscriptionDelay)(() => { + updateSubscription(updateSubscriptionCallbackUrl); + }); }); + // .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); + // }); + // }) diff --git a/services/Instagram/src/models/post.js b/services/Instagram/src/models/post.js index 0f020e96..dc273c89 100644 --- a/services/Instagram/src/models/post.js +++ b/services/Instagram/src/models/post.js @@ -1,3 +1,5 @@ +import {postType} from '../config'; + let date = i => new Date(parseInt(i) * 1000); /** @@ -61,7 +63,7 @@ let date = i => new Date(parseInt(i) * 1000); */ export const postFromInstagramMedia = i => ({ data: i, - type: 'instagram', + type: postType, date: date(i['created_time']), id: i.id }); diff --git a/services/Instagram/src/routers/auth.js b/services/Instagram/src/routers/auth.js index f4253def..4951a208 100644 --- a/services/Instagram/src/routers/auth.js +++ b/services/Instagram/src/routers/auth.js @@ -30,6 +30,7 @@ export function authCallback() { 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; diff --git a/services/Instagram/src/routers/callback.js b/services/Instagram/src/routers/callback.js index a8718596..e7a3a72b 100644 --- a/services/Instagram/src/routers/callback.js +++ b/services/Instagram/src/routers/callback.js @@ -3,6 +3,8 @@ 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{ @@ -14,8 +16,12 @@ export function callbackGet() { 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", diff --git a/services/Instagram/webpack.config.babel.js b/services/Instagram/webpack.config.babel.js index 6cc7cd5b..d37b77d8 100644 --- a/services/Instagram/webpack.config.babel.js +++ b/services/Instagram/webpack.config.babel.js @@ -5,6 +5,7 @@ import HtmlWebpackPlugin from 'html-webpack-plugin'; module.exports = { entry: './src/app.js', + watch: true, //resolve: { // modulesDirectories: [ // './node_modules'