mirror of
				https://gitlab.com/Alamantus/Readlebee.git
				synced 2025-11-04 10:17:03 +01:00 
			
		
		
		
	Merge branch 'master' of https://gitlab.com/Alamantus/Readlebee
This commit is contained in:
		
						commit
						7ea08aaf6a
					
				
					 16 changed files with 925 additions and 525 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -8,4 +8,5 @@ dev/
 | 
			
		|||
config.json
 | 
			
		||||
*.sqlite*
 | 
			
		||||
*.db
 | 
			
		||||
.dbversion
 | 
			
		||||
.dbversion
 | 
			
		||||
.DS_Store
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ An attempt at a viable alternative to Goodreads
 | 
			
		|||
  - Features we feel are essential to the project. Anything beyond the scope should be discussed for later and not prioritized.
 | 
			
		||||
- [Dependencies Stack](https://gitlab.com/Alamantus/Readlebee/wikis/Dependencies-Stack)
 | 
			
		||||
  - A list of dependencies used in the project and a short explanation of what each of them are for.
 | 
			
		||||
- [Contrubution Guidelines](./CONTRIBUTING.md)
 | 
			
		||||
- [Contribution Guidelines](./CONTRIBUTING.md)
 | 
			
		||||
  - Subject to change but important to follow. Includes a basic code of conduct.
 | 
			
		||||
- [Project chat via Gitter](https://gitter.io/Readlebee)
 | 
			
		||||
  - Real-time discussion about the project.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
import { ViewController } from '../controller';
 | 
			
		||||
import { ShelvesController } from '../shelves/controller';
 | 
			
		||||
 | 
			
		||||
export class SearchController extends ViewController {
 | 
			
		||||
  constructor(state, emit, i18n) {
 | 
			
		||||
| 
						 | 
				
			
			@ -13,6 +14,7 @@ export class SearchController extends ViewController {
 | 
			
		|||
      done: true,
 | 
			
		||||
      results: [],
 | 
			
		||||
      openModal: null,
 | 
			
		||||
      showShelves: false,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.emit = emit;
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +46,19 @@ export class SearchController extends ViewController {
 | 
			
		|||
    return this.state.openModal;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get hasFetchedShelves() {
 | 
			
		||||
    return typeof this.appState.viewStates.shelves !== 'undefined'
 | 
			
		||||
    && typeof this.appState.viewStates.shelves.myShelves !== 'undefined'
 | 
			
		||||
    && this.appState.viewStates.shelves.myShelves.length > 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get shelves() {
 | 
			
		||||
    if (this.hasFetchedShelves) {
 | 
			
		||||
      return this.appState.viewStates.shelves.myShelves;
 | 
			
		||||
    }
 | 
			
		||||
    return [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  set openModal(modalId) {
 | 
			
		||||
    this.state.openModal = modalId;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -85,4 +100,29 @@ export class SearchController extends ViewController {
 | 
			
		|||
 | 
			
		||||
    return Promise.resolve();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  showShelves () {
 | 
			
		||||
    const shelfController = new ShelvesController(this.appState, this.i18n);
 | 
			
		||||
    let shelvesPromise;
 | 
			
		||||
    if (shelfController.state.myShelves.length < 1) {
 | 
			
		||||
      console.log('getting');
 | 
			
		||||
      shelvesPromise = shelfController.getUserShelves();
 | 
			
		||||
    } else {
 | 
			
		||||
      shelvesPromise = Promise.resolve();
 | 
			
		||||
    }
 | 
			
		||||
    shelvesPromise.then(() => {
 | 
			
		||||
      console.log(shelfController.state.myShelves);
 | 
			
		||||
      this.showShelves = true;
 | 
			
		||||
      this.emit('render');
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addToShelf(bookData, shelfId) {
 | 
			
		||||
    const shelfController = new ShelvesController(this.appState, this.i18n);
 | 
			
		||||
    shelfController.addItemToShelf(bookData, shelfId).then(result => {
 | 
			
		||||
      console.log(result);
 | 
			
		||||
      this.showShelves = false;
 | 
			
		||||
      this.emit('render');
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +16,6 @@ export const searchView = (state, emit, i18n) => {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  // Returning an array in a view allows non-shared parent HTML elements.
 | 
			
		||||
  // This one doesn't have the problem right now, but it's good to remember.
 | 
			
		||||
  return [
 | 
			
		||||
    html`<h1 class="title">${__('search.header')}</h1>`,
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +44,7 @@ export const searchView = (state, emit, i18n) => {
 | 
			
		|||
 | 
			
		||||
    // Search Options Section
 | 
			
		||||
    html`<section class="flex one two-700">
 | 
			
		||||
      <div>
 | 
			
		||||
      ${/*<div>
 | 
			
		||||
        ${modal('searchSourceInfo', controller, [
 | 
			
		||||
          html`<p>
 | 
			
		||||
            ${__('search.search_source.help.text')}
 | 
			
		||||
| 
						 | 
				
			
			@ -91,7 +90,8 @@ export const searchView = (state, emit, i18n) => {
 | 
			
		|||
            </option>
 | 
			
		||||
          </select>
 | 
			
		||||
        </label>
 | 
			
		||||
      </div>
 | 
			
		||||
      </div>*/'' // Temporarily comment out the source chooser so I can focus on just Inventaire
 | 
			
		||||
      }
 | 
			
		||||
      <div>
 | 
			
		||||
          ${__('search.search_by.label')}<br>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,21 +8,28 @@ export const resultDetails = (searchController, result, emit = () => {}) => {
 | 
			
		|||
  const { __ } = searchController.i18n;
 | 
			
		||||
  const modalId = `result_${result.uri}`;
 | 
			
		||||
 | 
			
		||||
  const hasReviews = typeof result.averageRating !== 'undefined' && typeof result.numberOfReviews !== 'undefined';
 | 
			
		||||
 | 
			
		||||
  const buttonHTML = html`<label for=${modalId} class="pseudo button">
 | 
			
		||||
    <span data-tooltip="${__('interaction.average_rating')}: ${result.averageRating}">
 | 
			
		||||
      ${starRating(result.averageRating)}
 | 
			
		||||
    </span>  
 | 
			
		||||
    <span style="margin-left:10px;" data-tooltip=${__('interaction.reviews_written')}>
 | 
			
		||||
      <span style="margin-right:8px;"><i class="icon-chat"></i></span>
 | 
			
		||||
      <span>${result.numberOfReviews}</span>
 | 
			
		||||
    </span>
 | 
			
		||||
    ${!hasReviews
 | 
			
		||||
      ? __('search.no_reviews')
 | 
			
		||||
      : html`<span data-tooltip="${__('interaction.average_rating')}: ${result.averageRating}">
 | 
			
		||||
        ${starRating(result.averageRating)}
 | 
			
		||||
      </span>  
 | 
			
		||||
      <span style="margin-left:10px;" data-tooltip=${__('interaction.reviews_written')}>
 | 
			
		||||
        <span style="margin-right:8px;"><i class="icon-chat"></i></span>
 | 
			
		||||
        <span>${result.numberOfReviews}</span>
 | 
			
		||||
      </span>`
 | 
			
		||||
    }
 | 
			
		||||
    <br />
 | 
			
		||||
    <small>${__('search.click_for_details')}</small>
 | 
			
		||||
  </label>`;
 | 
			
		||||
  
 | 
			
		||||
  const tabNames = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty'];
 | 
			
		||||
  
 | 
			
		||||
  const modalContent = html`<article class="flex">
 | 
			
		||||
    <div class="sixth-700" style="text-align:center;">
 | 
			
		||||
      <h4>Covers</h4>
 | 
			
		||||
      <h4>${__('search.covers')}</h4>
 | 
			
		||||
      ${typeof result.covers === 'undefined'
 | 
			
		||||
        ? html`<span style="font-size:3em;"><i class="icon-loading animate-spin"></i></span>`
 | 
			
		||||
        : html`<div class="tabs ${typeof tabNames[result.covers.length - 1] !== 'undefined' ? tabNames[result.covers.length - 1] : null}">
 | 
			
		||||
| 
						 | 
				
			
			@ -55,33 +62,41 @@ export const resultDetails = (searchController, result, emit = () => {}) => {
 | 
			
		|||
      }
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="two-third-700">
 | 
			
		||||
      <h4>${__('interaction.average_rating')}</h4>
 | 
			
		||||
      <span data-tooltip="${result.averageRating}">${starRating(result.averageRating)}</span>
 | 
			
		||||
      ${!hasReviews
 | 
			
		||||
        ? html`<h4>${__('search.no_reviews')}</h4>`
 | 
			
		||||
        : html`<h4>${__('interaction.average_rating')}</h4>
 | 
			
		||||
        <span data-tooltip="${result.averageRating}">${starRating(result.averageRating)}</span>
 | 
			
		||||
 | 
			
		||||
      <div class="flex">
 | 
			
		||||
        <div>
 | 
			
		||||
          <h4>Top Reviews</h4>
 | 
			
		||||
        <div class="flex">
 | 
			
		||||
          <div>
 | 
			
		||||
            <h4>Top Reviews</h4>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div>
 | 
			
		||||
            <a href="/book/${result.uri}" class="small button">
 | 
			
		||||
              <span style="margin-right:8px;"><i class="icon-chat"></i></span>
 | 
			
		||||
              <span>${result.numberOfReviews}</span>
 | 
			
		||||
              <span>${__('search.see_interaction_details')}</span>
 | 
			
		||||
            </a>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
          <a href="/book/${result.uri}" class="small button">
 | 
			
		||||
            <span style="margin-right:8px;"><i class="icon-chat"></i></span>
 | 
			
		||||
            <span>${result.numberOfReviews}</span>
 | 
			
		||||
            <span>${__('search.see_interaction_details')}</span>
 | 
			
		||||
          </a>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      ${result.reviews.map(review => {
 | 
			
		||||
        return reviewCard(searchController, review);
 | 
			
		||||
      })}
 | 
			
		||||
        ${(typeof result.reviews !== 'undefined' && Array.isArray(result.reviews) ? result.reviews : []).map(review => {
 | 
			
		||||
          return reviewCard(searchController, review);
 | 
			
		||||
        })}`
 | 
			
		||||
      }
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="sixth-700">
 | 
			
		||||
      <p>
 | 
			
		||||
        <span data-tooltip=${__('interaction.add')}>
 | 
			
		||||
          <button class="success">
 | 
			
		||||
            <i class="icon-plus"></i>
 | 
			
		||||
          </button>
 | 
			
		||||
        </span>
 | 
			
		||||
        <button class="success" onclick=${() => searchController.showShelves()}>
 | 
			
		||||
          <i class="icon-plus"></i> <span>${__('interaction.add')}</span>
 | 
			
		||||
        </button>
 | 
			
		||||
      </p>
 | 
			
		||||
      ${!searchController.showShelves ? null : html`<ul>${searchController.shelves.map(shelf => {
 | 
			
		||||
        return html`<li>
 | 
			
		||||
          <button class="pseudo" onclick=${() => searchController.addToShelf({source: 'inventaire', uri: result.uri}, shelf.id)}>
 | 
			
		||||
            ${shelf.name}
 | 
			
		||||
          </button>
 | 
			
		||||
        </li>`;
 | 
			
		||||
      })}</ul>`}
 | 
			
		||||
      <p>
 | 
			
		||||
        <a class="small button" href=${result.link} target="_blank">
 | 
			
		||||
          ${__('search.see_book_details')}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,4 +42,37 @@ export class ShelvesController extends ViewController {
 | 
			
		|||
      this.state.loadedShelves[this.targetShelf] = shelf;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async addItemToShelf (book, shelfId) {
 | 
			
		||||
    let bookId;
 | 
			
		||||
    if (typeof book.source !== 'undefined' && typeof book.uri !== 'undefined') {
 | 
			
		||||
      const bookSearchResult = await fetch('/api/books/getId', {
 | 
			
		||||
        method: 'POST',
 | 
			
		||||
        headers: {
 | 
			
		||||
          'Content-Type': 'application/json',
 | 
			
		||||
        },
 | 
			
		||||
        body: JSON.stringify(book),
 | 
			
		||||
      }).then(response => response.json());
 | 
			
		||||
      
 | 
			
		||||
      if (typeof bookSearchResult.error !== 'undefined') {
 | 
			
		||||
        console.error(bookSearchResult);
 | 
			
		||||
        return bookSearchResult;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      bookId = bookSearchResult;
 | 
			
		||||
    } else {
 | 
			
		||||
      bookId = book.id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return fetch('/api/shelf/addItem', {
 | 
			
		||||
      method: 'POST',
 | 
			
		||||
      headers: {
 | 
			
		||||
        'Content-Type': 'application/json',
 | 
			
		||||
      },
 | 
			
		||||
      body: JSON.stringify({
 | 
			
		||||
        shelfId,
 | 
			
		||||
        bookId,
 | 
			
		||||
      }),
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +71,7 @@ export const shelfView = (shelvesController, emit) => {
 | 
			
		|||
            ${__('shelves.owned_by')}
 | 
			
		||||
            ${shelf.user === null
 | 
			
		||||
              ? __('shelves.you')
 | 
			
		||||
              : `<a href="/profile?user=${shelf.user.handle}" title=${shelf.user.handle}>${shelf.user.name}</a>`}
 | 
			
		||||
              : html`<a href="/profile?user=${shelf.user.handle}" title="${shelf.user.handle}">${shelf.user.name}</a>`}
 | 
			
		||||
            </span>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="third sixth-700">
 | 
			
		||||
| 
						 | 
				
			
			@ -87,9 +87,7 @@ export const shelfView = (shelvesController, emit) => {
 | 
			
		|||
        return html`<article class="card">
 | 
			
		||||
          <footer>
 | 
			
		||||
            <div class="flex one twelve-700">
 | 
			
		||||
              <div class="full sixth-700">
 | 
			
		||||
                <img src=${shelfItem.coverURL} alt="cover ${shelfItem.coverEdition}" />
 | 
			
		||||
              </div>
 | 
			
		||||
              <img class="full sixth-700" src=${shelfItem.coverURL} alt="cover ${shelfItem.coverEdition}" />
 | 
			
		||||
              <div class="full half-700">
 | 
			
		||||
                <h3>${shelfItem.title}</h3>
 | 
			
		||||
                <span>${shelfItem.author}</span>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										40
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										40
									
								
								package.json
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -7,9 +7,10 @@
 | 
			
		|||
  "author": "Robbie Antenesse <dev@alamantus.com>",
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "dev": "concurrently --kill-others \"npm run watch-js\" \"npm run serve\"",
 | 
			
		||||
    "dev": "concurrently --kill-others \"npm run watch-app\" \"npm run watch-server\"",
 | 
			
		||||
    "start": "npm run build && cross-env NODE_ENV=production npm run serve",
 | 
			
		||||
    "watch-js": "parcel watch app/index.html --out-dir public --no-hmr --no-cache",
 | 
			
		||||
    "watch-app": "parcel watch app/index.html --out-dir public --no-hmr --no-cache",
 | 
			
		||||
    "watch-server": "onchange -i -k \"server/**/*.js*\" -- npm run serve",
 | 
			
		||||
    "serve": "node server/index.js",
 | 
			
		||||
    "build": "npm run process-images && npm run bundle",
 | 
			
		||||
    "bundle": "parcel build app/index.html --out-dir public --no-source-maps --no-cache",
 | 
			
		||||
| 
						 | 
				
			
			@ -21,35 +22,36 @@
 | 
			
		|||
    "choo-devtools": "^3.0.3",
 | 
			
		||||
    "concurrently": "^5.1.0",
 | 
			
		||||
    "faker": "^4.1.0",
 | 
			
		||||
    "onchange": "^7.0.2",
 | 
			
		||||
    "rimraf": "^3.0.1",
 | 
			
		||||
    "sequelize-erd": "https://github.com/Alamantus/sequelize-erd.git"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "autoprefixer": "^9.7.4",
 | 
			
		||||
    "autoprefixer": "^9.8.5",
 | 
			
		||||
    "babel-polyfill": "^6.26.0",
 | 
			
		||||
    "choo": "^7.1.0",
 | 
			
		||||
    "cross-env": "^7.0.0",
 | 
			
		||||
    "fastify": "^2.11.0",
 | 
			
		||||
    "fastify-caching": "^5.0.0",
 | 
			
		||||
    "fastify-compress": "^2.0.0",
 | 
			
		||||
    "fastify-cookie": "^3.5.0",
 | 
			
		||||
    "fastify-helmet": "^3.0.2",
 | 
			
		||||
    "fastify-jwt": "^1.2.1",
 | 
			
		||||
    "fastify-plugin": "^1.6.0",
 | 
			
		||||
    "fastify-static": "^2.6.0",
 | 
			
		||||
    "cross-env": "^7.0.2",
 | 
			
		||||
    "fastify": "^3.1.1",
 | 
			
		||||
    "fastify-caching": "^6.0.1",
 | 
			
		||||
    "fastify-compress": "^3.2.2",
 | 
			
		||||
    "fastify-cookie": "^4.0.2",
 | 
			
		||||
    "fastify-helmet": "^4.0.2",
 | 
			
		||||
    "fastify-jwt": "^2.1.2",
 | 
			
		||||
    "fastify-plugin": "^2.0.3",
 | 
			
		||||
    "fastify-static": "^3.2.0",
 | 
			
		||||
    "make-promises-safe": "^5.1.0",
 | 
			
		||||
    "marked": "^0.8.0",
 | 
			
		||||
    "marked": "^1.1.1",
 | 
			
		||||
    "mysql2": "^2.1.0",
 | 
			
		||||
    "node-fetch": "^2.6.0",
 | 
			
		||||
    "nodemailer": "^6.4.2",
 | 
			
		||||
    "nodemailer": "^6.4.10",
 | 
			
		||||
    "parcel-bundler": "^1.12.4",
 | 
			
		||||
    "parcel-plugin-goodie-bag": "^2.0.0",
 | 
			
		||||
    "pg": "^7.18.1",
 | 
			
		||||
    "pg": "^8.3.0",
 | 
			
		||||
    "pg-hstore": "^2.3.3",
 | 
			
		||||
    "picnic": "^6.5.2",
 | 
			
		||||
    "sass": "^1.25.0",
 | 
			
		||||
    "sequelize": "^5.21.3",
 | 
			
		||||
    "sharp": "^0.24.0",
 | 
			
		||||
    "sqlite": "^3.0.3"
 | 
			
		||||
    "sass": "^1.26.10",
 | 
			
		||||
    "sequelize": "^6.3.3",
 | 
			
		||||
    "sharp": "^0.25.4",
 | 
			
		||||
    "sqlite3": "^5.0.0"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -93,6 +93,27 @@ class BooksController {
 | 
			
		|||
      ),
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async createBookReference (bookReferencesModel, source, uri) {
 | 
			
		||||
    const inventaire = new Inventaire(this.language);
 | 
			
		||||
    const bookData = await inventaire.getBookData(uri);
 | 
			
		||||
    return await bookReferencesModel.create({
 | 
			
		||||
      values: {
 | 
			
		||||
        name: bookData.name,
 | 
			
		||||
        description: bookData.description,
 | 
			
		||||
        sources: {
 | 
			
		||||
          [source]: uri,
 | 
			
		||||
        },
 | 
			
		||||
        covers: bookData.covers.map(cover => {
 | 
			
		||||
          return {
 | 
			
		||||
            sourceId: uri,
 | 
			
		||||
            url: cover.url,
 | 
			
		||||
          };
 | 
			
		||||
        }),
 | 
			
		||||
        locale: this.language,
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = BooksController;
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +76,7 @@ class SearchController {
 | 
			
		|||
 | 
			
		||||
    let extraReferences = [];
 | 
			
		||||
    if (urisToCheck.length > 0) {
 | 
			
		||||
      // Need to figure this out
 | 
			
		||||
      extraReferences = await this.searchReferencesBySourceCodes(source, urisToCheck);
 | 
			
		||||
    }
 | 
			
		||||
    return [
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +140,7 @@ class SearchController {
 | 
			
		|||
    const sourceJSONKey = `"${source}"`;  // Enable searching withing JSON column.
 | 
			
		||||
    return await this.models.BookReference.findOne({
 | 
			
		||||
      where: {
 | 
			
		||||
        source: {
 | 
			
		||||
        sources: {
 | 
			
		||||
          [sourceJSONKey]: {  // Where the object key is the source
 | 
			
		||||
            [Op.eq]: sourceId,
 | 
			
		||||
          },
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +157,7 @@ class SearchController {
 | 
			
		|||
    return await this.models.BookReference.findAll({
 | 
			
		||||
      where: {
 | 
			
		||||
        [Op.or]: sourceIds.map(sourceId => ({
 | 
			
		||||
          source: {
 | 
			
		||||
          sources: {
 | 
			
		||||
            [sourceJSONKey]: sourceId,
 | 
			
		||||
          },
 | 
			
		||||
        })),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,11 +26,13 @@ const fastifyNodemailer = (fastify, options, next) => {
 | 
			
		|||
  try {
 | 
			
		||||
    transporter = createTransport(options);
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    fastify.decorate('canEmail', false);
 | 
			
		||||
    return next(err);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fastify
 | 
			
		||||
    .decorate('nodemailer', transporter)
 | 
			
		||||
    .decorate('canEmail', true)
 | 
			
		||||
    .addHook('onClose', close);
 | 
			
		||||
 | 
			
		||||
  next();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,13 +21,14 @@ const fp = require('fastify-plugin');
 | 
			
		|||
const Sequelize = require('sequelize');
 | 
			
		||||
 | 
			
		||||
function plugin (fastify, options) {
 | 
			
		||||
  const instance = options.instance || 'sequelize';
 | 
			
		||||
  const autoConnect = options.autoConnect || true;
 | 
			
		||||
  const { config, registerModels } = options;
 | 
			
		||||
  const instance = config.instance || 'sequelize';
 | 
			
		||||
  const autoConnect = config.autoConnect || true;
 | 
			
		||||
 | 
			
		||||
  delete options.instance;
 | 
			
		||||
  delete options.autoConnect;
 | 
			
		||||
  delete config.instance;
 | 
			
		||||
  delete config.autoConnect;
 | 
			
		||||
 | 
			
		||||
  const sequelize = new Sequelize(options);
 | 
			
		||||
  const sequelize = new Sequelize(config);
 | 
			
		||||
 | 
			
		||||
  if (autoConnect) {
 | 
			
		||||
    return sequelize.authenticate().then(decorate);
 | 
			
		||||
| 
						 | 
				
			
			@ -39,6 +40,7 @@ function plugin (fastify, options) {
 | 
			
		|||
 | 
			
		||||
  function decorate () {
 | 
			
		||||
    fastify.decorate(instance, sequelize);
 | 
			
		||||
    fastify.decorate('models', registerModels(sequelize));
 | 
			
		||||
    fastify.addHook('onClose', (fastifyInstance, done) => {
 | 
			
		||||
      sequelize.close()
 | 
			
		||||
        .then(done)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,9 +7,11 @@
 | 
			
		|||
    "menu_login": "Log In / Create Account",
 | 
			
		||||
    "menu_account": "My Profile",
 | 
			
		||||
    "menu_logout": "Log Out",
 | 
			
		||||
    "menu_shelves": "My Shelves",
 | 
			
		||||
    "footer_repo": "Repo",
 | 
			
		||||
    "footer_chat": "Chat",
 | 
			
		||||
    "change_language": "Language"
 | 
			
		||||
    "change_language": "Language",
 | 
			
		||||
    "error": "Oops, something went wrong!"
 | 
			
		||||
  },
 | 
			
		||||
  "home": {
 | 
			
		||||
    "logged_out": {
 | 
			
		||||
| 
						 | 
				
			
			@ -72,11 +74,14 @@
 | 
			
		|||
      "author": "Author"
 | 
			
		||||
    },
 | 
			
		||||
    "loading": "Loading...",
 | 
			
		||||
    "no_reviews": "No Reviews yet",
 | 
			
		||||
    "click_for_details": "Show Book Details",
 | 
			
		||||
    "no_results": "None Found",
 | 
			
		||||
    "no_results_suggestion": "If you're expecting book data, go and help fill out the Inventaire database!",
 | 
			
		||||
    "people_header": "People",
 | 
			
		||||
    "series_header": "Series",
 | 
			
		||||
    "books_header": "Books",
 | 
			
		||||
    "covers": "Covers",
 | 
			
		||||
    "see_interaction_details": "See All Interactions",
 | 
			
		||||
    "see_book_details": "See Book Details"
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			@ -88,6 +93,17 @@
 | 
			
		|||
    "average_rating": "Average Rating",
 | 
			
		||||
    "reviews_written": "Total Reviews Written"
 | 
			
		||||
  },
 | 
			
		||||
  "shelves": {
 | 
			
		||||
    "title": "My Shelves",
 | 
			
		||||
    "loading": "Loading Shelf...",
 | 
			
		||||
    "you": "You",
 | 
			
		||||
    "owned_by": "Owned By",
 | 
			
		||||
    "no_shelf_selected": "No shelf selected.",
 | 
			
		||||
    "not_logged_in": "You're not logged in."
 | 
			
		||||
  },
 | 
			
		||||
  "review": {
 | 
			
		||||
    "review_of": "Review of"
 | 
			
		||||
  },
 | 
			
		||||
  "api": {
 | 
			
		||||
    "not_logged_in": "You are not logged in.",
 | 
			
		||||
    "already_logged_in": "You are already logged in! You cannot create an account or log in again.",
 | 
			
		||||
| 
						 | 
				
			
			@ -124,6 +140,11 @@
 | 
			
		|||
        "invalid_token": "User not logged in: The stored token is not a valid token.",
 | 
			
		||||
        "renewed_token": "User logged in, and the token has been renewed."
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "shelf": {
 | 
			
		||||
      "get": {
 | 
			
		||||
        "invalid_id": "Invalid ID specified"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -45,10 +45,14 @@ switch (fastify.siteConfig.db_engine) {
 | 
			
		|||
    sequelizeConfig.password = fastify.siteConfig.db_password;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
fastify.register(require('./fastify-plugins/fastify-sequelize'), sequelizeConfig);
 | 
			
		||||
fastify.register(require('./fastify-plugins/fastify-sequelize'), {
 | 
			
		||||
  config: sequelizeConfig,
 | 
			
		||||
  registerModels: require('./sequelize/models'),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
if (!fastify.siteConfig.email_host || !fastify.siteConfig.email_username) {
 | 
			
		||||
  console.warn('###\nNo email server set up. You will not be able to send emails without entering your email configuration.\n###');
 | 
			
		||||
  fastify.decorate('canEmail', false);
 | 
			
		||||
} else {
 | 
			
		||||
  fastify.register(require('./fastify-plugins/fastify-nodemailer'), {
 | 
			
		||||
    pool: true,
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +107,4 @@ fastify.listen(fastify.siteConfig.port, function (err, address) {
 | 
			
		|||
    fastify.log.error(err);
 | 
			
		||||
    process.exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fastify.decorate('canEmail', typeof fastify.nodemailer !== 'undefined');
 | 
			
		||||
  fastify.decorate('models', require('./sequelize/models')(fastify.sequelize));
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
const BooksController = require('../controllers/bookData');
 | 
			
		||||
const SearchController = require('../controllers/search');
 | 
			
		||||
 | 
			
		||||
async function routes(fastify, options) {
 | 
			
		||||
  fastify.get('/api/books', async (request, reply) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +17,33 @@ async function routes(fastify, options) {
 | 
			
		|||
 | 
			
		||||
    return await books.getInventaireCovers();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  fastify.post('/api/books/getId', async (request, reply) => {
 | 
			
		||||
    if (typeof request.body.source === 'undefined') {
 | 
			
		||||
      return reply.code(400).send({
 | 
			
		||||
        error: true,
 | 
			
		||||
        message: 'api.shelf.getId.missing_source',
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (typeof request.body.uri === 'undefined') {
 | 
			
		||||
      return reply.code(400).send({
 | 
			
		||||
        error: true,
 | 
			
		||||
        message: 'api.shelf.getId.missing_uri',
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const search = new SearchController(fastify.models);
 | 
			
		||||
    const existingBookReferences = await search.searchReferencesBySourceCodes(request.body.source, [request.body.uri]);
 | 
			
		||||
    if (existingBookReferences.length > 0) {
 | 
			
		||||
      return existingBookReferences[0].id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const books = new BooksController(request.body.source, request.body.uri, request.language);
 | 
			
		||||
    const newBookReference = await books.createBookReference(request.body.source, request.body.uri);
 | 
			
		||||
    console.log('created new bookreference', newBookReference);
 | 
			
		||||
    return newBookReference.id;
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = routes
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue