├── .gitignore
├── .jshintrc
├── .meteor
├── .finished-upgraders
├── .gitignore
├── .id
├── packages
├── platforms
├── release
└── versions
├── LICENSE
├── README.md
├── client
├── components
│ ├── App.jsx
│ ├── EmbedCode.jsx
│ ├── Generator.jsx
│ └── Preview.jsx
├── home.css
├── home.html
├── home.jsx
└── stylesheets
│ └── semantic_ui
│ ├── custom.semantic.json
│ └── theme.config.import.less
├── private
└── icon.svg
├── public
├── favicon-32x32.png
└── images
│ └── logo.png
└── server
├── cron.js
├── methods.js
├── package_info.js
└── routes.js
/.gitignore:
--------------------------------------------------------------------------------
1 | **/semantic_ui/definitions
2 | **/semantic_ui/themes
3 | **/semantic_ui/.custom.semantic.json
4 | **/semantic_ui/semantic.less
5 | **/semantic_ui/theme.import.less
6 | **/semantic_ui/site
7 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {"esnext": true}
2 |
--------------------------------------------------------------------------------
/.meteor/.finished-upgraders:
--------------------------------------------------------------------------------
1 | # This file contains information which helps Meteor properly upgrade your
2 | # app when you run 'meteor update'. You should check it into version control
3 | # with your project.
4 |
5 | notices-for-0.9.0
6 | notices-for-0.9.1
7 | 0.9.4-platform-file
8 | notices-for-facebook-graph-api-2
9 | 1.2.0-standard-minifiers-package
10 | 1.2.0-meteor-platform-split
11 | 1.2.0-cordova-changes
12 | 1.2.0-breaking-changes
13 |
--------------------------------------------------------------------------------
/.meteor/.gitignore:
--------------------------------------------------------------------------------
1 | local
2 |
--------------------------------------------------------------------------------
/.meteor/.id:
--------------------------------------------------------------------------------
1 | # This file contains a token that is unique to your project.
2 | # Check it into your repository along with the rest of this directory.
3 | # It can be used for purposes such as:
4 | # - ensuring you don't accidentally deploy one app on top of another
5 | # - providing package authors with aggregated statistics
6 |
7 | ndrg72nblyk814hasa
8 |
--------------------------------------------------------------------------------
/.meteor/packages:
--------------------------------------------------------------------------------
1 | # Meteor packages used by this project, one per line.
2 | # Check this file (and the other files in this directory) into your repository.
3 | #
4 | # 'meteor add' and 'meteor remove' will edit this file for you,
5 | # but you can also edit it by hand.
6 |
7 | meteor-base # Packages every Meteor app needs to have
8 | mobile-experience # Packages for a great mobile UX
9 | mongo # The database Meteor supports right now
10 | blaze-html-templates # Compile .html files into Meteor Blaze views
11 | session # Client-side reactive dictionary for your app
12 | jquery # Helpful client-side library
13 | tracker # Meteor's client-side reactive programming library
14 |
15 | standard-minifiers # JS/CSS minifiers run for production mode
16 | es5-shim # ECMAScript 5 compatibility for older browsers.
17 | ecmascript # Enable ECMAScript2015+ syntax in app code
18 |
19 | http
20 | check
21 | react
22 |
23 | momentjs:moment
24 | meteorhacks:ssr
25 | aldeed:collection2
26 | semantic:ui
27 | flemay:less-autoprefixer
28 |
--------------------------------------------------------------------------------
/.meteor/platforms:
--------------------------------------------------------------------------------
1 | server
2 | browser
3 |
--------------------------------------------------------------------------------
/.meteor/release:
--------------------------------------------------------------------------------
1 | METEOR@1.2.0.2
2 |
--------------------------------------------------------------------------------
/.meteor/versions:
--------------------------------------------------------------------------------
1 | aldeed:collection2@2.5.0
2 | aldeed:simple-schema@1.3.3
3 | autoupdate@1.2.3
4 | babel-compiler@5.8.24_1
5 | babel-runtime@0.1.4
6 | base64@1.0.4
7 | binary-heap@1.0.4
8 | blaze@2.1.3
9 | blaze-html-templates@1.0.1
10 | blaze-tools@1.0.4
11 | boilerplate-generator@1.0.4
12 | caching-compiler@1.0.0
13 | caching-html-compiler@1.0.2
14 | callback-hook@1.0.4
15 | check@1.0.6
16 | coffeescript@1.0.10
17 | cosmos:browserify@0.8.2
18 | ddp@1.2.2
19 | ddp-client@1.2.1
20 | ddp-common@1.2.1
21 | ddp-server@1.2.1
22 | deps@1.0.9
23 | diff-sequence@1.0.1
24 | ecmascript@0.1.5
25 | ecmascript-collections@0.1.6
26 | ejson@1.0.7
27 | es5-shim@4.1.13
28 | fastclick@1.0.7
29 | flemay:less-autoprefixer@1.2.0
30 | geojson-utils@1.0.4
31 | hot-code-push@1.0.0
32 | html-tools@1.0.5
33 | htmljs@1.0.5
34 | http@1.1.1
35 | id-map@1.0.4
36 | jquery@1.11.4
37 | jsx@0.2.3
38 | launch-screen@1.0.4
39 | livedata@1.0.15
40 | logging@1.0.8
41 | meteor@1.1.9
42 | meteor-base@1.0.1
43 | meteorhacks:ssr@2.1.2
44 | minifiers@1.1.7
45 | minimongo@1.0.10
46 | mobile-experience@1.0.1
47 | mobile-status-bar@1.0.6
48 | momentjs:moment@2.10.6
49 | mongo@1.1.2
50 | mongo-id@1.0.1
51 | npm-mongo@1.4.39_1
52 | observe-sequence@1.0.7
53 | ordered-dict@1.0.4
54 | promise@0.5.0
55 | random@1.0.4
56 | react@0.14.1_1
57 | react-meteor-data@0.2.3
58 | react-runtime@0.14.1_1
59 | react-runtime-dev@0.14.1
60 | react-runtime-prod@0.14.1
61 | reactive-dict@1.1.2
62 | reactive-var@1.0.6
63 | reload@1.1.4
64 | retry@1.0.4
65 | routepolicy@1.0.6
66 | semantic:ui@2.1.6
67 | semantic:ui-data@2.1.6
68 | session@1.1.1
69 | spacebars@1.0.7
70 | spacebars-compiler@1.0.7
71 | standard-minifiers@1.0.1
72 | templating@1.1.4
73 | templating-tools@1.0.0
74 | tracker@1.0.9
75 | ui@1.0.8
76 | underscore@1.0.4
77 | url@1.0.5
78 | webapp@1.2.2
79 | webapp-hashing@1.0.5
80 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Matthew Wood, Sung Won Cho, Tomas Trescak
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Meteor Icon
2 |
3 | A simple Atmosphere badge to display the information about your package anywhere.
4 |
5 | Winner of [2015 Meteor Global Distributed Hackathon]
6 | (http://info.meteor.com/blog/meteor-global-distributed-hackathon-winners) for
7 | the Best Submission Using Less Than 100 Lines of JavaScript.
8 |
9 |
10 | ## How to use
11 |
12 | 1. Go to [icon.meteor.com](http://icon.meteor.com/) and type in your package name.
13 | 2. Grab the markdown code and embed into GitHub or anywhere you want!
14 |
15 | ```md
16 | []
17 | (https://atmospherejs.com/tomi/upload-jquery)
18 | ```
19 |
20 |
21 | ## Why?
22 |
23 | Meteor Icon builds better package ecosystem for Meteor by making it quicker
24 | and easier to communicate package information.
25 |
26 | Inspired by [nodei.co](https://nodei.co/), Meteor Icon cleanly displays
27 | information including the install command, latest version, last published date,
28 | popularity measures.
29 |
30 |
31 | ## Options
32 |
33 | * Default
34 |
35 | []
36 | (https://atmospherejs.com/tomi/upload-jquery)
37 |
38 | *http://icon.meteor.com/package/tomi:upload-jquery*
39 |
40 | * Graph and scores
41 |
42 | Draw on graph and scores by appending passing a query parameter graph with true
43 |
44 | []
45 | (https://atmospherejs.com/tomi/upload-jquery)
46 |
47 | *http://icon.meteor.com/package/tomi:upload-jquery?graph=true*
48 |
49 |
50 | ## Video Demo
51 |
52 | []
53 | (https://www.youtube.com/watch?v=NQm33Wg1HHg "Demo")
54 |
55 |
56 | ## Collaborators
57 |
58 | From [Meteor Sydney](http://www.meetup.com/Meteor-Sydney/):
59 |
60 | * [sungwoncho](https://github.com/sungwoncho/)
61 | * [tomitrescak](https://github.com/tomitrescak)
62 | * [woody1990](https://github.com/woody1990)
63 |
64 |
65 | ## Roadmap
66 |
67 | - [x] Caching by comparing timestamp
68 | - [x] Support for MDG packages
69 |
70 |
71 | ## Contributing
72 |
73 | Feel free to open an issue with a feature request or a bug.
74 |
75 |
76 | ## License
77 |
78 | MIT
79 |
--------------------------------------------------------------------------------
/client/components/App.jsx:
--------------------------------------------------------------------------------
1 | App = React.createClass({
2 | getInitialState() {
3 | return {
4 | packageOnDisplay: 'tomi:upload-jquery'
5 | };
6 | },
7 |
8 | handleGenerate(packageName) {
9 | this.setState({packageOnDisplay: packageName});
10 | },
11 |
12 | render() {
13 | return (
14 |
15 |
16 |
17 |

18 |
19 | Meteor Icon
20 |
21 | A smarter way to communicate with package users
22 |
23 |
24 |
25 |
26 | You can get one for your package today.
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
40 |
41 | );
42 | }
43 | });
44 |
--------------------------------------------------------------------------------
/client/components/EmbedCode.jsx:
--------------------------------------------------------------------------------
1 | EmbedCode = React.createClass({
2 | componentDidUpdate() {
3 | Meteor.call('validatePackageName', this.props.packageOnDisplay, (err, res) => {
4 | if (err) {
5 | console.log('Error while validating package name', err);
6 | return;
7 | }
8 |
9 | if (! res) {
10 | this.refs.embedCode.value = 'There is no package by that name.';
11 | }
12 | });
13 | },
14 |
15 | getEmbedCode() {
16 | var packageName = this.props.packageOnDisplay;
17 | var iconPath = Meteor.absoluteUrl(`package/${packageName}`);
18 |
19 | var base = `[](https://atmospherejs.com/`;
20 | if (packageName.split(':')[1]) {
21 | return base.concat(`${packageName.replace(/\:/, '/')})`);
22 | } else {
23 | return base.concat(`meteor/${packageName.split(':')[0]})`);
24 | }
25 | },
26 |
27 | selectCode() {
28 | this.refs.embedCode.select();
29 | },
30 |
31 | render() {
32 | return (
33 |
40 | );
41 | }
42 | });
43 |
--------------------------------------------------------------------------------
/client/components/Generator.jsx:
--------------------------------------------------------------------------------
1 | Generator = React.createClass({
2 | handleSubmit(e) {
3 | e.preventDefault();
4 | var packageName = this.refs.packageName.value.trim();
5 | this.props.onCodeGenerate(packageName);
6 | },
7 |
8 | render() {
9 | return (
10 |
24 | );
25 | }
26 | });
27 |
--------------------------------------------------------------------------------
/client/components/Preview.jsx:
--------------------------------------------------------------------------------
1 | Preview = React.createClass({
2 | getIconPath() {
3 | return `package/${this.props.packageOnDisplay}`;
4 | },
5 |
6 | render() {
7 | return (
8 |
9 |
})
10 |
11 | );
12 | }
13 | });
14 |
--------------------------------------------------------------------------------
/client/home.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #EFEFEF;
3 | }
4 |
5 | #main-box {
6 | padding-top: 5em;
7 | }
8 |
9 | #logo {
10 | user-drag: none;
11 | -moz-user-select: none;
12 | -webkit-user-drag: none;
13 | user-select: none;
14 | -moz-user-select: none;
15 | -webkit-user-select: none;
16 | }
17 |
18 | footer {
19 | padding: 20px 0;
20 | text-align: center;
21 | }
22 |
23 | .package-name {
24 | padding: 5px 8px;
25 | font-size: 15px;
26 | width: 270px;
27 | }
28 |
29 | .generator {
30 | margin: 3em 0;
31 | }
32 |
--------------------------------------------------------------------------------
/client/home.html:
--------------------------------------------------------------------------------
1 |
2 | Meteor Icon
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/client/home.jsx:
--------------------------------------------------------------------------------
1 | Meteor.startup(function () {
2 | ReactDOM.render(, document.getElementById('home'));
3 | });
4 |
--------------------------------------------------------------------------------
/client/stylesheets/semantic_ui/custom.semantic.json:
--------------------------------------------------------------------------------
1 | {
2 | "definitions": {
3 | "accordion": true,
4 | "ad": true,
5 | "api": true,
6 | "breadcrumb": true,
7 | "button": true,
8 | "card": true,
9 | "checkbox": true,
10 | "colorize": true,
11 | "comment": true,
12 | "container": true,
13 | "dimmer": true,
14 | "divider": true,
15 | "dropdown": true,
16 | "embed": true,
17 | "feed": true,
18 | "flag": true,
19 | "form": true,
20 | "grid": true,
21 | "header": true,
22 | "icon": true,
23 | "image": true,
24 | "input": true,
25 | "item": true,
26 | "label": true,
27 | "list": true,
28 | "loader": true,
29 | "menu": true,
30 | "message": true,
31 | "modal": true,
32 | "nag": true,
33 | "popup": true,
34 | "progress": true,
35 | "rail": true,
36 | "rating": true,
37 | "reset": true,
38 | "reveal": true,
39 | "search": true,
40 | "segment": true,
41 | "shape": true,
42 | "sidebar": true,
43 | "site": true,
44 | "state": true,
45 | "statistic": true,
46 | "step": true,
47 | "sticky": true,
48 | "tab": true,
49 | "table": true,
50 | "transition": true,
51 | "visibility": true,
52 | "visit": true
53 | },
54 | "themes": {
55 | "amazon": false,
56 | "basic": false,
57 | "bookish": false,
58 | "bootstrap3": false,
59 | "chubby": false,
60 | "classic": false,
61 | "colored": false,
62 | "default": true,
63 | "duo": false,
64 | "fixed": false,
65 | "flat": false,
66 | "github": false,
67 | "gmail": false,
68 | "instagram": false,
69 | "material": false,
70 | "pulsar": false,
71 | "raised": false,
72 | "resetcss": false,
73 | "round": false,
74 | "rtl": false,
75 | "striped": false,
76 | "timeline": false,
77 | "twitter": false
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/client/stylesheets/semantic_ui/theme.config.import.less:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | ████████╗██╗ ██╗███████╗███╗ ███╗███████╗███████╗
4 | ╚══██╔══╝██║ ██║██╔════╝████╗ ████║██╔════╝██╔════╝
5 | ██║ ███████║█████╗ ██╔████╔██║█████╗ ███████╗
6 | ██║ ██╔══██║██╔══╝ ██║╚██╔╝██║██╔══╝ ╚════██║
7 | ██║ ██║ ██║███████╗██║ ╚═╝ ██║███████╗███████║
8 | ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚══════╝
9 |
10 | */
11 |
12 | /*******************************
13 | Theme Selection
14 | *******************************/
15 |
16 | /* To override a theme for an individual element
17 | specify theme name below
18 | */
19 |
20 | /* Global */
21 | @site : 'default';
22 | @reset : 'default';
23 |
24 | /* Elements */
25 | @button : 'default';
26 | @container : 'default';
27 | @divider : 'default';
28 | @flag : 'default';
29 | @header : 'default';
30 | @icon : 'default';
31 | @image : 'default';
32 | @input : 'default';
33 | @label : 'default';
34 | @list : 'default';
35 | @loader : 'default';
36 | @rail : 'default';
37 | @reveal : 'default';
38 | @segment : 'default';
39 | @step : 'default';
40 |
41 | /* Collections */
42 | @breadcrumb : 'default';
43 | @form : 'default';
44 | @grid : 'default';
45 | @menu : 'default';
46 | @message : 'default';
47 | @table : 'default';
48 |
49 | /* Modules */
50 | @accordion : 'default';
51 | @checkbox : 'default';
52 | @dimmer : 'default';
53 | @dropdown : 'default';
54 | @embed : 'default';
55 | @modal : 'default';
56 | @nag : 'default';
57 | @popup : 'default';
58 | @progress : 'default';
59 | @rating : 'default';
60 | @search : 'default';
61 | @shape : 'default';
62 | @sidebar : 'default';
63 | @sticky : 'default';
64 | @tab : 'default';
65 | @transition : 'default';
66 |
67 | /* Views */
68 | @ad : 'default';
69 | @card : 'default';
70 | @comment : 'default';
71 | @feed : 'default';
72 | @item : 'default';
73 | @statistic : 'default';
74 |
75 | /*******************************
76 | Folders
77 | *******************************/
78 |
79 | /* Path to theme packages */
80 | @themesFolder : 'themes';
81 |
82 | /* Path to site override folder */
83 | @siteFolder : 'site';
84 |
85 |
86 | /*******************************
87 | Import Theme
88 | *******************************/
89 |
90 | @import "theme.import.less";
91 |
92 | /* End Config */
--------------------------------------------------------------------------------
/private/icon.svg:
--------------------------------------------------------------------------------
1 |
36 |
--------------------------------------------------------------------------------
/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungwoncho/meteor-icon/6422b8a172de622c8af0d697f438d408fee6c2af/public/favicon-32x32.png
--------------------------------------------------------------------------------
/public/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sungwoncho/meteor-icon/6422b8a172de622c8af0d697f438d408fee6c2af/public/images/logo.png
--------------------------------------------------------------------------------
/server/cron.js:
--------------------------------------------------------------------------------
1 | // Avoid being shut down due to inactivity on *.meteor.com host
2 | // Make HTTP request to itself every 14 minutes
3 | Meteor.setInterval(function () {
4 | HTTP.get(Meteor.absoluteUrl(), function (err, res) {
5 | console.log('Made HTTP call to itself');
6 | });
7 | }, 840000);
8 |
--------------------------------------------------------------------------------
/server/methods.js:
--------------------------------------------------------------------------------
1 | function getAtmosphereEndpoint(pkgName) {
2 | return `https://atmospherejs.com/a/packages/findByNames\?names=${pkgName}`;
3 | }
4 |
5 | Meteor.methods({
6 | getPackageParams(name, options = {}) {
7 | check(name, String);
8 | this.unblock();
9 |
10 | // 6.30555 is an average width of alphanumeric characters in this fontSize
11 | function getWidth(name) {
12 | if (name) {
13 | return 255 + name.length * 6.305555555555555;
14 | } else {
15 | return 225;
16 | }
17 | }
18 |
19 | function getGraph(scores, width) {
20 | if (! scores) return;
21 |
22 | var min = 100000,
23 | max = 0,
24 | scoreGraph= [width + "," + 80, "0,80"],
25 | i=0;
26 |
27 | scores.forEach(function(data){
28 | if (data.score > max) {
29 | max = Math.ceil(data.score);
30 | } else if (data.score < min) {
31 | min = data.score;
32 | } else {
33 | min = min;
34 | }
35 | });
36 | scores.forEach(function(data){
37 | scoreGraph.push((i++*(width/5)) + "," +
38 | ((30 - 30 * ((data.score-min) / (max-min))) + 23));
39 | });
40 |
41 | var stars;
42 | if (max > 4) {
43 | stars = "★★★★★";
44 | } else {
45 | stars = "★★★★★".substring(0, max) + "☆☆☆☆".substring(0, 5-max);
46 | }
47 |
48 | return {
49 | scores: scoreGraph, lsv: max, stars: stars
50 | };
51 | }
52 |
53 | var pkg = PackageInfo.findOne({name: name});
54 | if (! pkg || pkg.lastRequestedAt < moment().subtract(24, 'hour')) {
55 | Meteor.call('refreshPackageInfo', name);
56 | pkg = PackageInfo.findOne({name: name}) || {};
57 | }
58 |
59 | var width = getWidth(pkg.name);
60 | var params = {
61 | name: pkg.name || '',
62 | version: pkg.latestVersion,
63 | pubDate: moment(pkg.latestPublishedAt).format('MMM Do YYYY'),
64 | starCount: pkg.starCount || 0,
65 | installCount: pkg.installCount || 0,
66 | width: width,
67 | totalWidth: width+2,
68 | logoOffset: width-75,
69 | };
70 |
71 | if (options.graph) {
72 | _.extend(params, getGraph(pkg.scores, width));
73 | }
74 |
75 | return params;
76 | },
77 | refreshPackageInfo(name) {
78 | check(name, String);
79 | this.unblock();
80 |
81 | console.log('Trying to refresh package info for', name);
82 | var endpoint = getAtmosphereEndpoint(name);
83 | var options = {headers: {'Accept': 'application/json'}};
84 | var response = HTTP.get(endpoint, options);
85 | var pkg = response.data[0];
86 |
87 | var atmosphereConn = DDP.connect('https://atmospherejs.com/');
88 | var scoresCollection = new Meteor.Collection('scores', atmosphereConn);
89 |
90 | // Since this DDP call is asynchronous,
91 | // users might see old data for a short while until the call completes
92 | atmosphereConn.subscribe('package/dailyScores', name, function () {
93 | var scores = scoresCollection.find().fetch();
94 | if (scores) {
95 | PackageInfo.update({name: name}, {$set: {scores: scores}});
96 | }
97 | });
98 |
99 | if (pkg) {
100 | var modifier = {
101 | $set: {
102 | latestVersion: pkg.latestVersion.version,
103 | latestPublishedAt: moment(pkg.latestVersion.published.$date).toDate(),
104 | starCount: pkg.starCount || 0,
105 | installCount: pkg['installs-per-year'] || 0,
106 | lastRequestedAt: new Date()
107 | }
108 | };
109 | return PackageInfo.upsert({name: pkg.name}, modifier);
110 | }
111 | },
112 | incrementRequestCount(name) {
113 | check(name, String);
114 | return PackageInfo.update({name: name}, {$inc: {requestCount: 1}});
115 | },
116 |
117 | // If Atmosphere responds with an empty array, the package is not published
118 | // there, and hence invalid
119 | validatePackageName(packageName) {
120 | this.unblock();
121 | var endpoint = `https://atmospherejs.com/a/packages/findByNames\?names=${packageName}`;
122 | var options = {headers: {'Accept': 'application/json'}};
123 | var response = HTTP.get(endpoint, options);
124 |
125 | return response.data.length > 0;
126 | }
127 | });
128 |
--------------------------------------------------------------------------------
/server/package_info.js:
--------------------------------------------------------------------------------
1 | PackageInfo = new Mongo.Collection('packageInfo');
2 |
3 | var schema = new SimpleSchema({
4 | name: {
5 | type: String
6 | },
7 | latestVersion: {
8 | type: String
9 | },
10 | latestPublishedAt: {
11 | type: Date
12 | },
13 | starCount: {
14 | type: Number
15 | },
16 | installCount: {
17 | type: Number
18 | },
19 | scores: {
20 | type: [Object],
21 | optional: true,
22 | blackbox: true
23 | },
24 | // Last time the icon for this package was requested
25 | lastRequestedAt: {
26 | type: Date,
27 | optional: true
28 | },
29 | requestCount: {
30 | type: Number,
31 | optional: true
32 | }
33 | });
34 |
35 | PackageInfo.attachSchema(schema);
36 |
--------------------------------------------------------------------------------
/server/routes.js:
--------------------------------------------------------------------------------
1 | // Check that a string is truthy and not equal to 'false'
2 | function toBoolean(str) {
3 | return str && str !== 'false';
4 | }
5 |
6 | WebApp.connectHandlers.use("/package", function (req, res) {
7 | var pkgName = req._parsedUrl.pathname.split('/')[2];
8 | var options = {
9 | graph: toBoolean(req.query.graph)
10 | };
11 |
12 | Meteor.call('getPackageParams', pkgName, options, function (err, params) {
13 | if (err) {
14 | console.log('Error occurred while getting package params', err);
15 | return res.end(err.message);
16 | }
17 |
18 | Meteor.call('incrementRequestCount', pkgName);
19 |
20 | SSR.compileTemplate('icon', Assets.getText('icon.svg'));
21 | res.writeHead(200, {"Content-Type": "image/svg+xml"});
22 | res.end(SSR.render('icon', params));
23 | });
24 | });
25 |
--------------------------------------------------------------------------------