Construiește un CMS nodePress

Ați creat cu succes un sistem plat de management al conținutului (CMS) utilizând Go. Următorul pas este să luați același ideal și să faceți un server web utilizând Node.js. Vă voi arăta cum să încărcați bibliotecile, să creați serverul și să rulați serverul.

Acest CMS va utiliza structura de date a site-ului așa cum este prezentată în primul tutorial, Construirea unui CMS: Structura și stilul. Prin urmare, descărcați și instalați această structură de bază într-un director nou.

Obținerea nodurilor și a bibliotecilor de noduri

Cel mai simplu mod de a instala Node.js pe un Mac este cu Homebrew. Dacă nu ați instalat încă Homebrew, tutorialul Homebrew Demystified: Ultimate Package Manager al OS X vă va arăta cum.

Pentru a instala Node.js cu Homebrew, tastați această instrucțiune într-un terminal:

instalați nodul de instalare

Când ați terminat, veți avea comenzi nod și npm complet instalate pe Mac. Pentru toate celelalte platforme, urmați instrucțiunile de pe site-ul Node.js.

Aveți grijă: mulți administratori de pachete instalează în prezent versiunea Node.js 0.10. Acest tutorial presupune că aveți versiunea 5.3 sau mai nouă. Puteți verifica versiunea dvs. tastând:

nod - versiune

nodul comanda execută interpretul JavaScript. NPM comanda este un manager de pachete pentru Node.js pentru a instala biblioteci noi, a crea noi proiecte și a rula scripturi pentru un proiect. Există multe tutoriale și cursuri excelente pe Node.js și NPM la Envato Tuts+.

Pentru a instala bibliotecile pentru serverul web, trebuie să executați aceste comenzi în programul Terminal.app sau iTerm.app:

npm instalare expres --save npm instalare ghidon --save npm instalare moment - salvare npm instalare marcat --save npm instalare jade --save npm instalare morgan --save

Express este o platformă de dezvoltare a aplicațiilor web. Este similar cu biblioteca GoWeb din Go. Handlebars este motorul templating pentru crearea paginilor. Momentul este o bibliotecă pentru a lucra cu date. Marcat este un mare Markdown pentru HTML Converter în JavaScript. Jade este o limbă de stenogramă HTML pentru crearea cu ușurință a codului HTML. Morgan este o bibliotecă de middleware pentru Express care generează fișierele log standard Apache.

O modalitate alternativă de a instala bibliotecile este să descărcați fișierele sursă pentru acest tutorial. După ce ați descărcat și dezarhivat, tastați acest lucru în directorul principal:

npm - instalează

Aceasta va instala tot ceea ce este necesar pentru a crea acest proiect.

nodePress.js

Acum puteți începe să creați serverul. În directorul superior al proiectului, creați un fișier numit nodePress.js, îl deschideți în editorul ales și începeți să adăugați următorul cod. Voi explica codul așa cum este plasat în fișier.

// Încărcați bibliotecile utilizate. // var fs = cer ('fs'); var path = necesită ("cale"); var child_process = necesită ('child_process'); var proces = solicită ("proces"); var express = necesită ("expres"); // http://expressjs.com/en/ var morgan = cere ('morgan'); // https://github.com/expressjs/morgan var Handlebars = necesită ("ghidon"); // http://handlebarsjs.com/ var moment = solicita ("moment"); // http://momentjs.com/ var marcat = cer ('marcat'); // https://github.com/chjj/marked var jade = cere ('jade'); // http://jade-lang.com/

Codul serverului pornește de la inițializarea tuturor bibliotecilor utilizate pentru crearea serverului. Bibliotecile care nu au un comentariu cu o adresă Web sunt bibliotecile interne Node.js.

// // Setarea variabilelor globale. // var părți = JSON.parse (fs.readFileSync ('./server.json', 'utf8')); var stilDir = process.cwd () + '/ teme / styling /' + părți ['CurrentStyling']; var layoutDir = process.cwd () + '/ teme / layouts /' + părți ['CurrentLayout']; var siteCSS = null; var siteScripts = null; var mainPage = null;

Apoi, am setat toate variabilele globale și configurațiile bibliotecii. Utilizarea variabilelor globale nu este cea mai bună practică de proiectare a software-ului, dar funcționează și face ca dezvoltarea rapidă.

părți variabila este o matrice de hash care contine toate partile unei pagini web. Fiecare pagină face trimitere la conținutul acestei variabile. Începe cu conținutul fișierului server.json găsit în partea de sus a directorului serverului.

Apoi folosesc informațiile din fișierul server.json pentru a crea căile complete către stiluri și aspecte directoarele utilizate pentru acest site.

Trei variabile sunt apoi setate la valori nula: siteCSS, siteScripts, și Pagina principală. Aceste variabile globale vor conține toate conținutul CSS, JavaScript și conținutul paginii principale. Aceste trei elemente sunt elementele cele mai solicitate de pe orice server web. De aceea, păstrarea lor în memorie economisește timp. În cazul în care ascunzătoare variabila în fișierul server.json este falsă, aceste elemente se re-citesc cu fiecare cerere.

marcat.setOptions (renderer: nou marcat.Renderer (), gfm: true, tabele: true, breaks: false, pedantic: false, sanitize: false, smartLists: true, smartypants: false);

Acest bloc de cod este pentru configurarea bibliotecii marcate pentru generarea HTML de la Markdown. În mare parte, mă întorc la masa și sprijinul smartLists.

părți ["layout"] = fs.readFileSync (layoutDir + '/template.html', 'utf8'); părți ["404"] = fs.readFileSync (stilDir + '/404.html', 'utf8'); părți ["footer"] = fs.readFileSync (stilDir + '/footer.html', 'utf8'); părți ["header"] = fs.readFileSync (styleDir + '/header.html', 'utf8'); părțile ["sidebar"] = fs.readFileSync (styleDir + '/ sidebar.html', 'utf8'); // Citiți în părțile paginii. // var partFiles = fs.readdirSync (părți ['Sitebase'] + "părți /"); partFiles.forEach (funcții (ele, index, array) părți [path.basename (ele, path.extname (ele))] = figPage (părți ['Sitebase'] + cale.extname (ele))););

părți variabila este în continuare încărcată cu piesele din stiluri și schemă directoare. Fiecare fișier din părți director în interiorul teren directorul este, de asemenea, încărcat în părți variabilă globală. Numele fișierului fără extensie este numele utilizat pentru a stoca conținutul fișierului. Aceste nume se extind în macroul Handlebars.

// // Ajutorul pentru setarea mânerului. // // // HandleBars Helper: save // ​​// Descriere: Acest helper se așteaptă la o """"unde numele // este salvat cu valoarea pentru expansiunile viitoare // și returnează // valoarea directă // Handlebars.registerHelper (" save ", funcția (nume, text) // // Variabilele locale. // var newName = "", newText = ""; // // A se vedea dacă numele și textul se află în primul argument // cu un | .. Dacă da, extrageți-le corect.În caz contrar, // folosiți numele și textul argumentele date. // if (nume.indexOf ("|")> 0) var parts = nume.split ("|"); newName = părți [0]; = nume; newText = text; // // Înregistrați noul ajutor.// Handlebars.registerHelper (newName, function () return newText;; // // Returnează textul // returnText;) ; // // HandleBars Helper: date // // Descriere: Acest ajutor returnează data // pe baza formatului dat. // Handlebars.registerHelper ("data", funcția (dFormat) moment retur () format. dFormat);); // // HandleBars Helper: cdate // // Descriere: Acest ajutor returnează data dată // într-un format bazat pe format // dat. // Handlebars.registerHelper ("cdate", funcția (cTime, dFormat) moment de întoarcere (cTime) .format (dFormat););

Următoarea secțiune a codului definește asistenții pentru Handlebars pe care i-am definit pentru a fi utilizați în serverul web: Salvați, Data, și cdate. Ajutorul pentru salvare permite crearea de variabile în interiorul unei pagini. Această versiune acceptă versiunea goPress în care parametrul are numele și valoarea împreună separate de un "|". De asemenea, puteți specifica o salvare utilizând doi parametri. De exemplu:

salvați numele Richard Guay save "newName" "Richard Guay" Numele este: name newName este: newName

Acest lucru va produce aceleași rezultate. Prefer cea de-a doua abordare, dar biblioteca Handlebars din Go nu permite mai mult de un parametru.

Data și cdate ajutătorilor să formateze data curentă (Data) sau la o anumită dată (cdate) in conformitate cu moment.js regulile de formatare a bibliotecii. cdate helper se așteaptă ca data să devină primul parametru și să aibă format ISO 8601.

// // Creați și configurați serverul. // var nodePress = expres (); // // Configurează middleware. // nodePress.use (morgan ("combinat"))

Acum, codul creează o instanță Express pentru configurarea actualului motor de server. nodePress.use () funcția stabilește software-ul middleware. Middleware este orice cod care este servit la fiecare apel la server. Aici am creat biblioteca Morgan.js pentru a crea o ieșire logică corespunzătoare a serverului.

// // Definiți rutele. // nodePress.get ('/', functie (cerere, raspuns) setBasicHeader (raspuns); daca ((partile ["Cache"] == true) && (mainPage!  altceva mainPage = pagina ("principal"); answer.send (mainPage);); nodePress.get ('/ favicon.ico', funcție (cerere, răspuns) var opțiuni = rădăcină: părți ['Sitebase'] + 'images /', dotfiles: : Date.now (), 'x-sent': true; answer.set ("Content-Type", "image / ico"); , funcția (err) if (err) console.log (err); answer.status (err.status) .end (); altceva console.log ););); nodePress.get ('/ stylesheets.css', funcția (cerere, răspuns) response.set ("Content-Type", "text / css"); setBasicHeader (răspuns); ([Cache] == true) && (siteCSS! = null)) answer.send (siteCSS); altceva siteCSS = fs.readFileSync (părți ['Sitebase'] + 'css / final / final .css '); răspuns.send (siteCSS);); nodePress.get ('/ scripts.js', funcția (cerere, răspuns) response.set ("Content-Type", "text / javascript"); setBasicHeader (răspuns) = true) && (siteScripts! = null)) answer.send (siteScripts); altceva siteScripts = fs.readFileSync (părți ['Sitebase'] + 'js / final / final.js', 'utf8'); answer.send (siteScripts);); nodePress.get ('/ images /: image', funcția (cerere, răspuns) var opțiuni = rădăcină: părți ['Sitebase'] + 'images /', dotfiles: ': Date.now (),' x-trimis ': true; response.set ("Content-Type", "image /" + path.extname (request.params.image) .substr (1)); setBasicHeader (răspuns); answer.sendFile (request.params.image, opțiuni, funcție (err) if (err) console.log (err); answer.status (err.status) console.log ("Imaginea a fost trimisă: ', request.params.image);));); nodePress.get ('/ posts / blogs /: blog', funcția (cerere, răspuns) setBasicHeader (răspuns); answer.send (post ("blogs", request.params.blog, "index")) ; nodePress.get ('/ posts / blogs /: blog /: post', funcție (cerere, răspuns) setBasicHeader (răspuns); answer.send (post ("blogs", request.params.blog, request.params.post ));); nodePress.get ('/ posts / news /: news', functie (cerere, raspuns) setBasicHeader (raspunsul); reply.send (post "news", request.params.news, "index")) ; nodePress.get ('/ posts / news /: news /: post', funcție (cerere, răspuns) setBasicHeader (răspuns); answer.send (post ("news", request.params.news, request.params.post ));); nodePress.get ('/: page', funcție (cerere, răspuns) setBasicHeader (răspuns); answer.send (pagina (request.params.page));));

Această secțiune de cod definește toate rutele necesare pentru implementarea serverului web. Toate rutele rulează setBasicHeader () pentru a seta valorile corespunzătoare ale antetului. Toate solicitările pentru un tip de pagină vor evoca pagină() , în timp ce toate cererile pentru pagina de tip post va evoca posturi () funcţie.

Implicit pentru Tipul de conținut este HTML. Prin urmare, pentru CSS, JavaScript și imagini, Tipul de conținut este setat în mod explicit la valoarea corespunzătoare.

De asemenea, puteți defini rute cu a pune, șterge, și post REST verbe. Acest server simplu folosește numai obține verb.

// // Porniți serverul. // var adresaItems = părți ['ServerAddress'] split (':'); var server = nodePress.listen (adresaItems [2], funcție () var gazdă = server.address () adresa; var port = server.address () port; console.log ('nodePress asculta la http: /% s:% s ', gazdă, port););

Ultimul lucru pe care trebuie să-l faceți înainte de a defini diferitele funcții utilizate este să porniți serverul. Fișierul server.json conține numele DNS (aici, este gazdă locală) și portul pentru server. Odată analizat, serverul este asculta() funcția utilizează numărul portului pentru a porni serverul. Odată ce portul de server este deschis, scriptul înregistrează adresa și portul serverului.

// // Funcție: setBasicHeader // // Descriere: Această funcție va stabili informațiile despre antetul de bază // necesare. // // Intrări: // răspuns Obiectul răspunsului // funcția setBasicHeader (răspuns) response.append ("Cache-Control", "max-age = 2592000, cache"); answer.append ("Server", "nodePress - un CMS scris în nod din Custom Computer Tools: http://customct.com"); 

Prima funcție definită este setBasicHeader () funcţie. Această funcție stabilește antetul de răspuns pentru a informa browserul să cacheze pagina pentru o lună. De asemenea, îi spune browser-ului că serverul este un server nodePress. Dacă există alte valori standard ale antetului pe care le doriți, le veți adăuga aici cu response.append () funcţie.

// // Funcție: pagina // // Descriere: Această funcție procesează o cerere de pagină // // Intrări: // Pagina paginii solicitate // pagina funcției (pagina) // // Procesați pagina dată folosind standardul aspect. // retur (procesPage (părți ["layout"], părți ['Sitebase'] + "pagini /" + pagină)); 

pagină() funcția trimite șablonul de aspect pentru o pagină și locația paginii de pe server la proceseazapagina () funcţie.

// // Funcție: post // // Descriere: Această funcție procesează o cerere post // // Inputs: // type Tip post. // cat Categoria postului. // post Postul solicitat // funcția post (tip, pisică, post) // // procesează postul dat de tipul și numele postului. // întoarcere (processPage (părți ["layout"], părți ['Sitebase'] + "posturi" + tip + "/" + cat + "/" + post)); 

post() funcția este la fel ca pagină() , cu excepția faptului că posturile au mai multe elemente pentru a defini fiecare post. În această serie de servere, o postare conține a tip, categorie, și reală post. Tipul este fie bloguri sau știri. Categoria este flatcms. Deoarece acestea reprezintă nume de directoare, le puteți face oricând doriți. Doar potriviți denumirea cu ceea ce există în sistemul dvs. de fișiere.

// // Funcție: processPage // // Descriere: Această funcție procesează o pagină pentru CMS. // // Intrări: // layout Aspectul de utilizat pentru pagină. // pagina Calea către pagină pentru a reda. // function processPage (layout, page) // // Obtineti continutul paginilor si adaugati layout-ul. // var context = ; context = MergeRecursiv (context, părți); context ['conținut'] = cifraPage (pagină); context ['PageName'] = path.basename (pagina, path.extname (page)); // // Încărcați datele de pagină. // if (fileExists (pagina + ".json")) // // Încărcați fișierul de date al paginii și adăugați-l la structura de date. // context = MergeRecursiv (context, JSON.parse (fs.readFileSync (pagina + '.json', 'utf8')));  // // Codurile Handlebars Process. // var template = Handlebars.compile (aspect); var html = șablon (context); // Procesează toate codurile scurte. // html = processShortCodes (html); // // Rulați din nou prin mâneruri. // template = Handlebars.compile (html); html = șablon (context); // // Returnează rezultatele. // return (html); 

proceseazapagina () funcția obține aspectul și calea spre conținutul paginii pentru a face. Funcția începe prin a face o copie locală a părți variabila globală și adăugarea hashtag-ului "conținut" cu rezultatele apelului figurePage () funcţie. Apoi stabilește pagename hash valoare pentru numele paginii.

Această funcție compilează apoi conținutul paginii în șablonul de dispunere utilizând funcțiile Handlebars. După aceea, processShortCodes () funcția va extinde toate codurile scurte definite pe pagină. Apoi, motorul șablonului Handlebars trece din nou peste cod. Browserul primește apoi rezultatele.

// // Funcție: processShortCodes // // Descriere: Această funcție are un șir și // procesează toate codurile scurte din // șirul. // // Inputs: // content String to process // funcția processShortCodes (content) // // Crearea variabilei results. // var rezultatele = ""; // // Gaseste primul meci. / / var varăFirgin = / \ - \ [([^ \]] *) \] \ - / i; var = scregFind.exec (conținut); dacă (potrivire! = null) results + = content.substr (0, match.index); var scregNameArg = /(\w))(****/i; var parts = scregNameArg.exec (potrivire [1]); if (parts! = null) // // Găsiți eticheta de închidere. // var scregClose = nou RegExp ("\\ - \\ [\\ /" + părți [1] + "\\] \\ -"); var stânga = content.substr (match.index + 4 + parts [1] .length); var meci2 = scregClose.exec (stânga); if (match2! = null) // // Procesați textul de scurtătură inclus. // var enclosed = processShortCodes (content.substr (match.index + 4 + piese [1] .length, match2.index)); // Figurați dacă există argumente. // var args = ""; dacă (partss.length == 2) args = părți [2];  // // Executați codul scurt. // results + = coduri scurte [părți [1]] (args, închise); // Procesează restul codului pentru codurile scurte. // results + = processShortCodes (left.substr (match2.index + 5 + parts [1] .length));  altceva // // cod scurt. Returnați șirul complet. // results = content;  altceva // // Codul scurt este nevalid. Returnați șirul complet. // results = content;  altfel // Nu s-au găsit coduri scurte. Returnați șirul. // results = content;  retur (rezultate); 

processShortCodes () funcția ia conținutul paginii web ca un șir și caută toate codurile scurte. Un scurtcod este un bloc de cod similar tagului HTML. Un exemplu ar fi:

-[cutie]- 

Acesta este în interiorul unei cutii

-[/cutie]-

Acest cod are un shortcode pentru cutie în jurul unui paragraf HTML. În cazul în care utilizează codul HTML < și >, coduri scurte de utilizare -[ și ]-. După nume, un șir care conține argumente pentru codul scurt poate sau nu poate fi acolo.

processShortCodes () funcția găsește un scurtcod, își ia numele și argumentele, găsește sfârșitul pentru a obține conținutul, procesează conținutul pentru codurile scurte, execută codul scurt cu argumentele și conținutul, adaugă rezultatele la pagina finită și caută următorul cod scurt din restul paginii. Lovitura se efectuează prin apelarea recursivă a funcției.

// // Definiți matricea funcțiilor de coduri scurte. // var shortcodes = 'box': funcția (args, inside) return ("
"+ în interiorul +"
");" Column1 ": funcția (args, inside) return ("
"+ în interiorul +"
");" Column2 ": funcția (args, inside) return ("
"+ în interiorul +"
");" Column1of3 ": funcția (args, inside) return ("
"+ în interiorul +"
");" Column2of3 ": funcția (args, inside) return ("
"+ în interiorul +"
");" Column3of3 ": funcția (args, inside) return ("
"+ în interiorul +"
");" php ": funcția (args, inside) return ("
"+ în interiorul +"
");," js ": funcția (args, inside) return ("
"+ în interiorul +"
");" html ": funcția (args, inside) return ("
"+ în interiorul +"
");," css ": funcția (args, inside) return ("
"+ în interiorul +"
");;

Această secțiune următoare definește numerele scurte structura json care definește numele unui scurtcod asociat funcției sale. Toate funcțiile de coduri scurte acceptă doi parametri: args și interior. args este totul după numele și spațiul și înainte de închiderea etichetei. interior este tot conținutul etichetelor pentru deschiderea și închiderea codurilor scurte. Aceste funcții sunt de bază, dar puteți crea un scurtcod pentru a efectua orice vă puteți gândi în JavaScript.

// // Funcție: figurePage // // Descriere: Această funcție prezintă tipul de pagină // și încarcă conținutul corespunzător // returnează conținutul HTML pentru pagină. // // Intrări: // pagina Pagina pentru încărcarea conținutului. // function figurePage (pagina) var rezultat = ""; dacă (fileExists (pagina + ".html")) // // Este un fișier HTML. Citiți-l și trimiteți-l. // result = fs.readFileSync (pagina + ".html");  altfel dacă (fileExists (pagina + ".amber")) // // Este un fișier jade. Conversia la HTML și trimiterea ei pe. Eu // folosesc încă extensia de chihlimbar pentru compatibilitate // pentru a apăsa. // var jadeFun = jade.compileFile (pagina + ".amber", ); // Render funcția var rezultat = jadeFun ();  altfel dacă (fileExists (pagina + ".md")) // // Este un fișier de marcare. Conversia în HTML și trimiterea // pe ea. // rezultat = marcat (fs.readFileSync (pagina + ".md") toString ()); // // Codul de identificare al citatelor URI marcat cu un marcaj. // result = result.replace (/ \ & quot ;, g, "\" "); return (rezultat);

figurePage () funcția primește calea completă către o pagină de pe server. Această funcție apoi testează ca aceasta să fie o pagină HTML, Markdown sau Jade bazată pe extensie. Încă mai folosesc camera pentru Jade, deoarece aceasta era biblioteca pe care am folosit-o cu serverul goPress. Toate conținutul Markdown și Jade se traduc în HTML înainte de al transmite pe rutina de apelare. Deoarece procesorul Markdown traduce toate citatele ", Îi traduc înapoi înainte de al trece înapoi.

// // Funcție: fileExists // // Descriere: Această funcție returnează un adevărat boolean dacă // fișierul există. Altfel, fals. // // Intrări: // filePath Calea către un fișier într-un șir. // fișier funcționalExists (filePath) try returnați fs.statSync (filePath) .isFile ();  captură (err) return false; 

fisierul exista() funcția este un înlocuitor pentru fs.exists () funcția care facea parte din fs biblioteca Node.js. Utilizează fs.statSync () pentru a încerca să obțineți starea fișierului. Dacă se întâmplă o eroare, a fals este returnat. În caz contrar, se întoarce Adevărat.

// // Funcția: MergeRecursive // ​​// Descriere: Îmbinare recursivă a două obiecte // // Inputs: // obj1 Primul obiect de îmbinare // obj2 Cel de-al doilea obiect de îmbinare // Funcția MergeRecursive (obj1, obj2) pentru (var p în obj2) try // Proprietatea în obiectul destinație set; actualizați valoarea sa. dacă [obj2 [p] .constructor == Obiect) obj1 [p] = MergeRecursiv (obj1 [p], obj2 [p]);  altfel obj1 [p] = obj2 [p];  captură (e) // Proprietatea în obiectul destinație nu este setată; creați-le și setați valoarea. obj1 [p] = obj2 [p];  retur obj1; 

Ultima funcție este MergeRecursive () funcţie. Copiază cel de-al doilea obiect în primul obiect trecut. Folosesc acest lucru pentru a copia principalele părți variabilă globală într-o copie locală înainte de a adăuga părți specifice paginii.

Rularea locală

După salvarea fișierului, puteți rula serverul cu:

node nodePress.js

Alternativ, puteți utiliza funcția NPM script care este în fișierul package.json. Porniți astfel de scripturi npm:

npm start

Acest lucru va rula start script care este în interiorul fișierului package.json.

nodePress Server Pagina principală

Punctați browserul dvs. web la http: // localhost: 8080 și veți vedea pagina de mai sus. S-ar putea să fi observat că am adăugat mai mult cod de test la pagina principală. Toate modificările aduse paginilor sunt în descărcarea acestui tutorial. Ele sunt în mare parte doar câteva modificări minore pentru a testa mai complet funcționalitatea și pentru a se potrivi oricăror diferențe de la utilizarea diferitelor biblioteci. Cea mai notabilă diferență este că biblioteca Jade nu folosește $ pentru a numi variabile în timp ce Amber face.

Concluzie

Acum aveți exact același sistem de fișiere plate CMS în Go și Node.js. Acest lucru scarpină numai suprafața a ceea ce puteți construi cu această platformă. Experimentați și încercați ceva nou. Aceasta este cea mai bună parte a creării propriului server web.

Cod