Initial pass at S3 version

This commit is contained in:
Jay McCarthy 2013-10-07 14:10:32 -06:00
parent 0371ade45e
commit 4cb98ac8df
8 changed files with 298 additions and 72 deletions

View File

@ -1 +1,5 @@
/root /root
/static/pkgs
/static/pkg
/static/pkgs-all
*json

View File

@ -0,0 +1,31 @@
#lang racket/base
(require racket/file
racket/runtime-path
pkg/util
racket/string
web-server/http/id-cookie)
(define-runtime-path src ".")
(define-runtime-path root "root")
(make-directory* root)
(define secret-key
(make-secret-salt/file
(build-path root "secret.key")))
(define users-path (build-path root "users"))
(make-directory* users-path)
(define users.new-path (build-path root "users.new"))
(make-directory* users.new-path)
(github-client_id (file->string (build-path root "client_id")))
(github-client_secret (file->string (build-path root "client_secret")))
(define pkgs-path (build-path root "pkgs"))
(make-directory* pkgs-path)
(define static-path (build-path src "static"))
(define (author->list as)
(string-split as))
(provide (all-defined-out))

View File

@ -2,6 +2,7 @@
(module+ test (module+ test
(require rackunit)) (require rackunit))
(require web-server/http (require web-server/http
"common.rkt"
web-server/servlet-env web-server/servlet-env
racket/file racket/file
xml xml
@ -38,24 +39,6 @@
(define (salty str) (define (salty str)
(sha1 (open-input-string str))) (sha1 (open-input-string str)))
(define-runtime-path src ".")
(define-runtime-path root "root")
(make-directory* root)
(define secret-key
(make-secret-salt/file
(build-path root "secret.key")))
(define users-path (build-path root "users"))
(make-directory* users-path)
(define users.new-path (build-path root "users.new"))
(make-directory* users.new-path)
(github-client_id (file->string (build-path root "client_id")))
(github-client_secret (file->string (build-path root "client_secret")))
(define pkgs-path (build-path root "pkgs"))
(make-directory* pkgs-path)
(define id-cookie-name "pnrid") (define id-cookie-name "pnrid")
;; XXX Add a caching system ;; XXX Add a caching system
@ -159,9 +142,6 @@
new-v])) new-v]))
#f))) #f)))
(define (author->list as)
(string-split as))
(define (page/rss req) (define (page/rss req)
(define ps (define ps
(sort (map package-info (package-list)) (sort (map package-info (package-list))
@ -1132,7 +1112,7 @@
#:ssl-cert (build-path root "server-cert.pem") #:ssl-cert (build-path root "server-cert.pem")
#:ssl-key (build-path root "private-key.pem") #:ssl-key (build-path root "private-key.pem")
#:extra-files-paths #:extra-files-paths
(list (build-path src "static")) (list static-path)
#:servlet-regexp #rx"" #:servlet-regexp #rx""
#:port port)) #:port port))

View File

@ -0,0 +1,75 @@
#lang racket/base
(require web-server/http
racket/file
racket/match
json
net/url
racket/list
racket/path
racket/promise
meta/pkg-index/basic/main)
(define convert-to-json
(match-lambda
[(? list? l)
(map convert-to-json l)]
[(? string? s)
s]
[(? hash? ht)
(for/hash ([(k v) (in-hash ht)])
(values (convert-to-json-key k)
(convert-to-json v)))]
[(? number? n)
n]
[#f
#f]
[(? symbol? s)
(symbol->string s)]
[(? keyword? s)
(hasheq 'kw (keyword->string s))]
[x
(error 'convert-to-json "~e" x)]))
(define convert-to-json-key
(match-lambda
[(? string? s)
(string->symbol s)]
[(? symbol? s)
s]
[x
(error 'convert-to-json-key "~e" x)]))
(module+ main
(require "common.rkt")
(define pkg-list
(map path->string (directory-list pkgs-path)))
(define dispatch
(pkg-index/basic
(λ () pkg-list)
(λ (pkg-name)
(define ht (file->value (build-path pkgs-path pkg-name)))
(hash-set* ht
'name pkg-name
'tags (hash-ref ht 'tags empty)
'authors (author->list (hash-ref ht 'author ""))))))
(define (url->request u)
(make-request #"GET" (string->url u) empty
(delay empty) #f "1.2.3.4" 80 "4.3.2.1"))
(define (cache url file)
(define p (build-path static-path file))
(make-directory* (path-only p))
(with-output-to-file p
#:exists 'replace
(λ () ((response-output (dispatch (url->request url))) (current-output-port))))
(with-output-to-file (path-add-suffix p #".json")
#:exists 'replace
(λ () (write-json (convert-to-json (file->value p)))))
(void))
(cache "/pkgs" "pkgs")
(cache "/pkgs-all" "pkgs-all")
(for ([p (in-list pkg-list)])
(cache (format "/pkg/~a" p) (format "pkg/~a" p))))

View File

@ -0,0 +1,39 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<title>Packages &gt; Search &gt; !main-distribution &gt; !main-tests</title>
<script src="/sorttable.js"></script>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="/index.js"></script>
<link rel="stylesheet" type="text/css" href="/style.css">
</head>
<body>
<div class="breadcrumb">
<span><a href="/">Packages</a></span> &gt;
<span>Search</span>
<span id="logout">jay.mccarthy@gmail.com | <a href="/curate">curate</a> | <a href="/rss">rss</a> | <a href="/account/login">re-login</a> | <a href="/account/logout">logout</a></span>
</div>
<div id="menu"><form action="/query/search/!main-distribution/!main-tests">
<span class="menu_option"><input name="input_0" type="text"><input type="submit" value="Search"></span><span class="menu_option"><a href="/manage">Manage
Your Packages</a></span>
</form></div>
<p id="search_menu"></p>
<table class="packages sortable">
<thead><tr>
<th>Package</th>
<th>Authors</th>
<th>Description</th>
<th>Tags</th>
</tr></thead>
<tbody id="packages_table">
</tbody>
<tfoot></tfoot>
</table>
<div id="footer">
Powered by <a href="http://racket-lang.org">Racket</a> and a
mess of ugly JS. Written
by <a href="http://faculty.cs.byu.edu/~jay">Jay McCarthy</a>.
</div>
</body>
</html>

View File

@ -0,0 +1,116 @@
// xxx curate
// xxx rss
// xxx re-login
// xxx logout
// xxx what user am i
// xxx manage packages
$( document ).ready(function() {
var search_terms = [ "!main-tests", "!main-distribution" ];
function addfilterlink ( text, term ) {
return [$('<a>', { text: text,
href: "javascript:void(0)",
click: function () {
search_terms.push(term);
evaluate_search();
} } ),
" "
];
};
function removefilterlink ( text, term ) {
return [$('<a>', { text: text,
href: "javascript:void(0)",
click: function () {
search_terms = $.grep( search_terms, function (v) { return v != term } );
evaluate_search();
} } ),
" "
];
};
$.getJSON( "/pkgs-all.json", function( resp ) {
var names = [];
$.each(resp, function(key, value) { names.push(key) });
var snames = names.sort(function(a,b) {
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
})
var now = new Date().getTime() / 1000;
$.each( snames,
function (name_i) {
var name = snames[name_i];
var value = resp[name];
$('<tr>',
{ class: ((now - (60*60*24*2)) < value['last-updated'] ? "recent" : "old") }).
data( "obj", value).append(
$('<td>').html( $('<a>', { text: value['name'],
href: "javascript:void(0)",
click: function () {
// XXX open up a subwindow
console.log(value);
} } ) ),
$('<td>').append( $.map( value['authors'], function ( author, i ) {
return addfilterlink ( author, "author:" + author );
} ) ),
$('<td>').text( value['description'] ),
$('<td>').append( $.map( value['tags'], function ( tag, i ) {
return addfilterlink ( tag, tag );
} ) )).appendTo('#packages_table');
});
evaluate_search();
});
function evaluate_search_term( value, term ) {
if ( term == ":error:") {
return value['checksum-error'];
} else if ( term == ":no-tag:") {
return value['tags'].length == 0;
} else if ( term.substring(0, 5) == "ring:") {
return value['ring'] == term.substring(5);
} else if ( term.substring(0, 7) == "author:") {
return ($.inArray( term.substring(7), value['authors'] ) != -1);
} else if ( term.charAt(0) == "!" ) {
return ! evaluate_search_term( value, term.substring(1) );
} else if ( $.inArray( term, value['tags']) != -1 ) {
return true;
} else {
return false;
}
}
function evaluate_search () {
$.each( $('#packages_table tr'), function (key, dom) {
var value = $(dom).data("obj");
var show = true;
for (termi in search_terms) {
var term = search_terms[termi];
if ( ! evaluate_search_term( value, term ) ) {
show = false;
}
}
if ( show ) {
$(dom).show();
} else {
$(dom).hide();
}
});
// xxx handle search button
// xxx update menu available
$("#search_menu").html("").append( $.map( search_terms, function ( term, i ) {
return removefilterlink ( term, term );
} ) );
$("#packages_table tr:visible:even").removeClass("even");
$("#packages_table tr:visible:odd").addClass("even");
};
});

View File

@ -1,42 +1,21 @@
function TocviewToggle(glyphid, id) {
var glyph = document.getElementById(glyphid);
var s = document.getElementById(id).style;
var expand = s.display == "none";
s.display = expand ? "block" : "none";
glyph.innerHTML = expand ? "&#9660;" : "&#9658;";
}
function ToggleOn(id) {
var s = document.getElementById(id).style;
var li = document.getElementById("li" + id);
s.display = "block";
li.setAttribute("class", "tab-selected");
}
function ToggleOff(id) {
var s = document.getElementById(id).style;
var li = document.getElementById("li" + id);
s.display = "none";
li.setAttribute("class", "");
}
/* /*
SortTable SortTable
version 2 version 2
7th April 2007 7th April 2007
Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/
Instructions: Instructions:
Download this file Download this file
Add <script src="sorttable.js"></script> to your HTML Add <script src="sorttable.js"></script> to your HTML
Add class="sortable" to any table you'd like to make sortable Add class="sortable" to any table you'd like to make sortable
Click on the headers to sort Click on the headers to sort
Thanks to many, many people for contributions and suggestions. Thanks to many, many people for contributions and suggestions.
Licenced as X11: http://www.kryogenix.org/code/browser/licence.html Licenced as X11: http://www.kryogenix.org/code/browser/licence.html
This basically means: do what you want with it. This basically means: do what you want with it.
*/ */
var stIsIE = /*@cc_on!@*/false; var stIsIE = /*@cc_on!@*/false;
sorttable = { sorttable = {
@ -47,19 +26,19 @@ sorttable = {
arguments.callee.done = true; arguments.callee.done = true;
// kill the timer // kill the timer
if (_timer) clearInterval(_timer); if (_timer) clearInterval(_timer);
if (!document.createElement || !document.getElementsByTagName) return; if (!document.createElement || !document.getElementsByTagName) return;
sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/;
forEach(document.getElementsByTagName('table'), function(table) { forEach(document.getElementsByTagName('table'), function(table) {
if (table.className.search(/\bsortable\b/) != -1) { if (table.className.search(/\bsortable\b/) != -1) {
sorttable.makeSortable(table); sorttable.makeSortable(table);
} }
}); });
}, },
makeSortable: function(table) { makeSortable: function(table) {
if (table.getElementsByTagName('thead').length == 0) { if (table.getElementsByTagName('thead').length == 0) {
// table doesn't have a tHead. Since it should have, create one and // table doesn't have a tHead. Since it should have, create one and
@ -70,9 +49,9 @@ sorttable = {
} }
// Safari doesn't support table.tHead, sigh // Safari doesn't support table.tHead, sigh
if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0];
if (table.tHead.rows.length != 1) return; // can't cope with two header rows if (table.tHead.rows.length != 1) return; // can't cope with two header rows
// Sorttable v1 put rows with a class of "sortbottom" at the bottom (as // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
// "total" rows, for example). This is B&R, since what you're supposed // "total" rows, for example). This is B&R, since what you're supposed
// to do is put them in a tfoot. So, if there are sortbottom rows, // to do is put them in a tfoot. So, if there are sortbottom rows,
@ -94,7 +73,7 @@ sorttable = {
} }
delete sortbottomrows; delete sortbottomrows;
} }
// work through each column and calculate its type // work through each column and calculate its type
headrow = table.tHead.rows[0].cells; headrow = table.tHead.rows[0].cells;
for (var i=0; i<headrow.length; i++) { for (var i=0; i<headrow.length; i++) {
@ -110,10 +89,10 @@ sorttable = {
// make it clickable to sort // make it clickable to sort
headrow[i].sorttable_columnindex = i; headrow[i].sorttable_columnindex = i;
headrow[i].sorttable_tbody = table.tBodies[0]; headrow[i].sorttable_tbody = table.tBodies[0];
dean_addEvent(headrow[i],"click", function(e) { dean_addEvent(headrow[i],"click", sorttable.innerSortFunction = function(e) {
if (this.className.search(/\bsorttable_sorted\b/) != -1) { if (this.className.search(/\bsorttable_sorted\b/) != -1) {
// if we're already sorted by this column, just // if we're already sorted by this column, just
// reverse the table, which is quicker // reverse the table, which is quicker
sorttable.reverse(this.sorttable_tbody); sorttable.reverse(this.sorttable_tbody);
this.className = this.className.replace('sorttable_sorted', this.className = this.className.replace('sorttable_sorted',
@ -126,7 +105,7 @@ sorttable = {
return; return;
} }
if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) {
// if we're already sorted by this column in reverse, just // if we're already sorted by this column in reverse, just
// re-reverse the table, which is quicker // re-reverse the table, which is quicker
sorttable.reverse(this.sorttable_tbody); sorttable.reverse(this.sorttable_tbody);
this.className = this.className.replace('sorttable_sorted_reverse', this.className = this.className.replace('sorttable_sorted_reverse',
@ -138,7 +117,7 @@ sorttable = {
this.appendChild(sortfwdind); this.appendChild(sortfwdind);
return; return;
} }
// remove sorttable_sorted classes // remove sorttable_sorted classes
theadrow = this.parentNode; theadrow = this.parentNode;
forEach(theadrow.childNodes, function(cell) { forEach(theadrow.childNodes, function(cell) {
@ -151,7 +130,7 @@ sorttable = {
if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }
sortrevind = document.getElementById('sorttable_sortrevind'); sortrevind = document.getElementById('sorttable_sortrevind');
if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
this.className += ' sorttable_sorted'; this.className += ' sorttable_sorted';
sortfwdind = document.createElement('span'); sortfwdind = document.createElement('span');
sortfwdind.id = "sorttable_sortfwdind"; sortfwdind.id = "sorttable_sortfwdind";
@ -173,18 +152,18 @@ sorttable = {
/* and comment out this one */ /* and comment out this one */
row_array.sort(this.sorttable_sortfunction); row_array.sort(this.sorttable_sortfunction);
row_array.reverse(); row_array.reverse();
tb = this.sorttable_tbody; tb = this.sorttable_tbody;
for (var j=0; j<row_array.length; j++) { for (var j=0; j<row_array.length; j++) {
tb.appendChild(row_array[j][1]); tb.appendChild(row_array[j][1]);
} }
delete row_array; delete row_array;
}); });
} }
} }
}, },
guessType: function(table, column) { guessType: function(table, column) {
// guess the type of a column based on its first non-blank row // guess the type of a column based on its first non-blank row
sortfn = sorttable.sort_alpha; sortfn = sorttable.sort_alpha;
@ -194,7 +173,7 @@ sorttable = {
if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) { if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) {
return sorttable.sort_numeric; return sorttable.sort_numeric;
} }
// check for a date: dd/mm/yyyy or dd/mm/yy // check for a date: dd/mm/yyyy or dd/mm/yy
// can have / or . or - as separator // can have / or . or - as separator
// can be mm/dd as well // can be mm/dd as well
possdate = text.match(sorttable.DATE_RE) possdate = text.match(sorttable.DATE_RE)
@ -217,17 +196,19 @@ sorttable = {
} }
return sortfn; return sortfn;
}, },
getInnerText: function(node) { getInnerText: function(node) {
// gets the text we want to use for sorting for a cell. // gets the text we want to use for sorting for a cell.
// strips leading and trailing whitespace. // strips leading and trailing whitespace.
// this is *not* a generic getInnerText function; it's special to sorttable. // this is *not* a generic getInnerText function; it's special to sorttable.
// for example, you can override the cell text with a customkey attribute. // for example, you can override the cell text with a customkey attribute.
// it also gets .value for <input> fields. // it also gets .value for <input> fields.
if (!node) return "";
hasInputs = (typeof node.getElementsByTagName == 'function') && hasInputs = (typeof node.getElementsByTagName == 'function') &&
node.getElementsByTagName('input').length; node.getElementsByTagName('input').length;
if (node.getAttribute("sorttable_customkey") != null) { if (node.getAttribute("sorttable_customkey") != null) {
return node.getAttribute("sorttable_customkey"); return node.getAttribute("sorttable_customkey");
} }
@ -262,7 +243,7 @@ sorttable = {
} }
} }
}, },
reverse: function(tbody) { reverse: function(tbody) {
// reverse the rows in a tbody // reverse the rows in a tbody
newrows = []; newrows = [];
@ -274,14 +255,14 @@ sorttable = {
} }
delete newrows; delete newrows;
}, },
/* sort functions /* sort functions
each sort function takes two parameters, a and b each sort function takes two parameters, a and b
you are comparing a[0] and b[0] */ you are comparing a[0] and b[0] */
sort_numeric: function(a,b) { sort_numeric: function(a,b) {
aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); aa = parseFloat(a[0].replace(/[^0-9.-]/g,''));
if (isNaN(aa)) aa = 0; if (isNaN(aa)) aa = 0;
bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); bb = parseFloat(b[0].replace(/[^0-9.-]/g,''));
if (isNaN(bb)) bb = 0; if (isNaN(bb)) bb = 0;
return aa-bb; return aa-bb;
}, },
@ -320,7 +301,7 @@ sorttable = {
if (dt1<dt2) return -1; if (dt1<dt2) return -1;
return 1; return 1;
}, },
shaker_sort: function(list, comp_func) { shaker_sort: function(list, comp_func) {
// A stable sort function to allow multi-level sorting of data // A stable sort function to allow multi-level sorting of data
// see: http://en.wikipedia.org/wiki/Cocktail_sort // see: http://en.wikipedia.org/wiki/Cocktail_sort
@ -350,7 +331,7 @@ sorttable = {
b++; b++;
} // while(swap) } // while(swap)
} }
} }
/* ****************************************************************** /* ******************************************************************

View File

@ -51,7 +51,7 @@ table.packages thead tr {
background: #FFCC66; background: #FFCC66;
} }
table.packages tbody tr:nth-child(2n) { table.packages tbody tr.even {
background: #F5F5DC; background: #F5F5DC;
} }