Migrate all the things

* Migrates from Mongo to Postgres.
* Migrates from JSPM to Webpack.
* Migrates from React to Vuejs.
* Migrates from Bootstrap to Bulma.

Also:
* Fixes rendering of meta data in the document head tag.
This commit is contained in:
Jonathan Cremin 2016-10-03 13:31:29 +01:00
parent 09706778d9
commit 7bb0497ff4
76 changed files with 6741 additions and 1760 deletions

View file

@ -1,19 +1,46 @@
import React from 'react';
import { renderPage } from '../lib/react-handler';
import { routes } from '../views/app';
import debuglog from 'debug';
import services from '../lib/services';
import render from '../lib/render';
import models from '../models';
const debug = debuglog('match.audio:share');
const recentQuery = {
include: [
{ model: models.artist },
{ model: models.match },
],
limit: 6,
order: [
['updatedAt', 'DESC'],
],
};
export default function* () {
const recents = [];
const docs = yield this.db.matches.find().sort({'created_at': -1}).limit(6).toArray();
docs.forEach(function(doc) {
let shares = Object.keys(doc.services).map(function (key) {return doc.services[key]; });
shares.some(function(item) {
if (item.service === doc._id.split('$$')[0]) { // eslint-disable-line no-underscore-dangle
recents.push(item);
return false;
}
});
});
const recentAlbums = yield models.album.findAll(recentQuery);
const recentTracks = yield models.track.findAll(recentQuery);
yield renderPage(routes, this.request.url, {recents: recents});
};
const initialState = {
recents: recentAlbums.map(album => album.toJSON())
.concat(recentTracks.map(track => track.toJSON()))
.sort((a, b) => a.createdAt < b.createdAt).slice(0, 6),
services: services.map(service => service.id),
};
const url = '/';
const html = yield render(url, initialState);
const head = {
title: `Share Music`,
shareUrl: `${this.request.origin}${url}`,
image: `${this.request.origin}/assets/images/logo-512.png`,
}
yield this.render('index', {
initialState,
head,
html,
});
}

View file

@ -1,14 +0,0 @@
import { parse } from 'url';
import request from 'superagent';
export default function* (next) {
const url = 'http://' + this.request.url.substr(8);
const parsed = parse(url);
if (parsed.host.match(/mzstatic\.com/)) {
const proxyResponse = yield request.get(url);
this.set(proxyResponse.headers);
this.body = proxyResponse.body;
} else {
yield next;
}
};

23
routes/recent.js Normal file
View file

@ -0,0 +1,23 @@
import models from '../models';
const recentQuery = {
include: [
{ model: models.artist },
{ model: models.match },
],
limit: 6,
order: [
['updatedAt', 'DESC'],
],
};
export default function* () {
const recentAlbums = yield models.album.findAll(recentQuery);
const recentTracks = yield models.track.findAll(recentQuery);
this.body = {
recents: recentAlbums.map(album => album.toJSON())
.concat(recentTracks.map(track => track.toJSON()))
.sort((a, b) => a.createdAt < b.createdAt).slice(0, 6),
};
}

View file

@ -1,49 +1,39 @@
import { parse } from 'url';
import co from 'co';
import lookup from '../lib/lookup';
import services from '../lib/services';
import { find, create, findMatchesAsync } from '../lib/share';
import debuglog from 'debug';
const debug = debuglog('match.audio:search');
module.exports = function* () {
export default function* () {
const url = parse(this.request.body.url);
this.assert(url.host, 400, {error: {message: 'You need to submit a url.'}});
const item = yield lookup(this.request.body.url);
this.assert(url.host, 400, { error: { message: 'You need to submit a url.' } });
this.assert(item, 400, {error: {message: 'No supported music found at that link :('}});
const music = yield lookup(this.request.body.url);
item.matched_at = new Date(); // eslint-disable-line camelcase
const matches = {};
matches[item.service] = item;
this.assert(music, 400, { error: { message: 'No supported music found at that link :(' } });
let share = yield find(music);
for (let service of services) {
if (service.id === item.service) {
continue;
}
matches[service.id] = {service: service.id};
if (!share) {
share = yield create(music);
findMatchesAsync(share);
}
yield this.db.matches.save({_id: item.service + '$$' + item.id, 'created_at': new Date(), services: matches});
this.body = item;
share = share.toJSON();
process.nextTick(() => {
for (let service of services) {
if (service.id === item.service) {
continue;
}
matches[service.id] = {service: service.id};
co(function* (){
const match = yield service.search(item);
match.matched_at = new Date(); // eslint-disable-line camelcase
const update = {};
update['services.' + match.service] = match;
yield this.db.matches.updateOne({_id: item.service + '$$' + item.id}, {'$set': update});
}.bind(this)).catch((err) => {
debug(err);
});
}
});
};
const unmatched = services.filter(service =>
!share.matches.find(match => match.service === service.id));
share.matches = share.matches.concat(unmatched.map((service) => {
return {
service: service.id,
matching: true,
};
}));
share.matches = share.matches.sort(a => !!a.externalId);
this.body = share;
}

View file

@ -1,72 +1,73 @@
import React from 'react';
import { renderPage } from '../lib/react-handler';
import { routes } from '../views/app';
import services from '../lib/services';
import co from 'co';
import render from '../lib/render';
import models from '../models';
import { find, create, findMatchesAsync } from '../lib/share';
function formatAndSort(matches, serviceId) {
matches = Object.keys(matches).map(function (key) {return matches[key]; });
matches.sort(function(a, b) {
return a.id && !b.id;
}).sort(function(a) {
return a.service !== serviceId;
});
return matches;
};
export default function* (serviceId, type, itemId, format) {
this.assert(type === 'album' || type === 'track', 400, { error: 'Invalid type' });
export default function* (serviceId, type, itemId, format, next) {
let matchedService;
services.some(function(service) {
matchedService = serviceId === service.id ? service : null;
return matchedService;
let share = yield models[type].findOne({
where: {
externalId: itemId,
},
include: [
{ model: models.match },
{ model: models.artist },
],
});
if (!matchedService || (type !== 'album' && type !== 'track')) {
return yield next;
}
if (!share) {
const matchedService = services.find(service => serviceId === service.id);
const music = yield matchedService.lookupId(itemId, type);
let doc = yield this.db.matches.findOne({_id: serviceId + '$$' + itemId});
if (!doc) {
const item = yield matchedService.lookupId(itemId, type);
this.assert(item.id, 404);
item.matched_at = new Date(); // eslint-disable-line camelcase
const matches = {};
matches[item.service] = item;
this.assert(music, 400, { error: { message: 'No supported music found at that link :(' } });
for (let service of services) {
if (service.id === item.service) {
continue;
}
matches[service.id] = {service: service.id};
share = yield find(music);
if (!share) {
share = yield create(music);
findMatchesAsync(share);
}
doc = {_id: item.service + '$$' + item.id, 'created_at': new Date(), services: matches};
yield this.db.matches.save(doc);
process.nextTick(() => {
for (let service of services) {
console.log(service.id);
if (service.id === item.service) {
continue;
}
matches[service.id] = {service: service.id};
co(function* (){
const match = yield service.search(item);
console.log(match.id);
match.matched_at = new Date(); // eslint-disable-line camelcase
const update = {};
update['services.' + match.service] = match;
yield this.db.matches.updateOne({_id: item.service + '$$' + item.id}, {'$set': update});
}.bind(this)).catch((err) => {
debug(err);
});
}
});
}
const shares = formatAndSort(doc.services, serviceId);
this.assert(share, 404);
const unmatched = services.filter(service =>
!share.matches.find(match => match.service === service.id));
share = share.toJSON();
share.matches = share.matches.concat(unmatched.map((service) => {
return {
service: service.id,
matching: true,
};
}));
share.matches = share.matches.sort(a => !a.externalId);
if (format === 'json') {
return this.body = {shares: shares};
}
this.body = share;
} else {
const initialState = {
item: share,
services: services.map(service => service.id),
};
this.body = yield renderPage(routes, this.request.url, {shares: shares});
};
const url = `/${serviceId}/${type}/${itemId}`;
const html = yield render(url, initialState);
const head = {
share,
title: `${share.name} by ${share.artist.name}`,
shareUrl: `${this.request.origin}${url}`,
image: share.matches.find(el => el.service === share.service).artworkLarge,
};
yield this.render('index', {
initialState,
share,
head,
html,
});
}
}