API can add and view items/images

main
Ashelyn Dawn 5 years ago
parent 502532262e
commit db6366550d

@ -0,0 +1,11 @@
const router = require('express-promise-router')()
const db = require('../db')
router.get('/:uuid/:size', async (req, res) => {
const image = await db.item.getImage(req.params.uuid, req.params.size)
res.set('Content-Type', image.mime_type)
res.end(image.file)
})
module.exports = router

@ -2,6 +2,8 @@ const router = require('express-promise-router')()
const pg = require('../db/pg') const pg = require('../db/pg')
router.use('/users/', require('./users')) router.use('/users/', require('./users'))
router.use('/items/', require('./items'))
router.use('/images/', require('./images'))
router.get('/', (req, res)=>{ router.get('/', (req, res)=>{
res.json({test: true}) res.json({test: true})

@ -5,34 +5,57 @@ const db = require('../db')
const {validationResult} = require('express-validator') const {validationResult} = require('express-validator')
const validate = require('./middleware/validators') const validate = require('./middleware/validators')
const upload = multer({ const upload = require('multer')({
storage: multer.memoryStorage(), storage: require('multer').memoryStorage(),
limits: { limits: {
files: 1, files: 1,
fileSize: 500000 fileSize: 500000
} }
}) })
router.post('/', parseJSON, validate.urlSlugRestrictions('slug'), async (req, res) => { router.get('/', async (req, res) => {
const items = await db.item.findAll()
res.json(items)
})
const newItemValidators = [
validate.urlSlugRestrictions,
validate.publishedBool,
validate.priceCentsInt,
validate.nameRestrictions,
validate.descriptionRestrictions
]
router.post('/', parseJSON, newItemValidators, async (req, res) => {
const errors = validationResult(req) const errors = validationResult(req)
if(!errors.isEmpty()) if(!errors.isEmpty())
return res.status(422).json({errors: errors.array()}) return res.status(422).json({errors: errors.array()})
const item = await db.item.create(
req.body.name,
req.body.urlslug,
req.body.description,
req.body.price_cents,
req.body.published
)
res.json(item)
// TODO: Create session
res.json(user)
}) })
router.post('/:uuid/images', upload.single('image'), async (req, res) => { router.post('/:uuid/images', upload.single('image'), async (req, res) => {
// TODO: Use the real user when we have authentication
req.user = {
uuid: '5e17388d-612b-4ea8-b76d-620a7bb24824'
}
// Handle either image upload body or JSON body // Handle either image upload body or JSON body
try { try {
if(req.file) if(req.file)
await db.item.addImage(req.params.uuid, req.file.buffer) await db.item.addImage(req.params.uuid, req.file.buffer, req.user.uuid)
else else
await db.item.addImage(req.params.uuid, await b64.decode(req.body.image.split(',')[1])) await db.item.addImage(req.params.uuid, await b64.decode(req.body.image.split(',')[1]), req.user.uuid)
} catch (error) { } catch (error) {
error.status = 500 error.status = 500
throw error throw error

@ -18,5 +18,16 @@ validators.checkEmailNotUsed = body('email').custom(async email=>{
if(user) throw new Error('Email already in use') if(user) throw new Error('Email already in use')
}) })
validators.urlSlugRestrictions = field => body(field).isString().isLength({min: 3, max: 20}).matches(/[-a-z0-9_]*/i) validators.urlSlugRestrictions = body('urlslug').isString().isLength({min: 3, max: 20}).matches(/^[-a-z0-9_]*$/i)
.withMessage('Slug can be between 3-20 characters long, and can include letters, numbers, dash or underscore') .withMessage('Slug can be between 3-20 characters long, and can include letters, numbers, dash or underscore')
validators.nameRestrictions = body('name').isString()
.withMessage('Name required. Must be a string')
validators.descriptionRestrictions = body('description').isString()
.withMessage('Description required. Must be a string')
validators.publishedBool = body('published').isBoolean()
validators.priceCentsInt = body('price_cents').isInt().custom(price => price > 0)
.withMessage('Price must be a positive integer')

@ -1,3 +1,4 @@
module.exports = { module.exports = {
user: require('./models/user') user: require('./models/user'),
item: require('./models/item')
} }

@ -1,3 +1,4 @@
module.exports = [ module.exports = [
...require('./user') ...require('./user'),
...require('./item')
] ]

@ -22,7 +22,7 @@ module.exports = [{
'published' 'published'
], ],
collections: [ collections: [
{name: 'images', mapId: 'imageMap', columnPrefix: 'imageColumn_'} {name: 'images', mapId: 'imageMap', columnPrefix: 'image_'}
] ]
},{ },{
mapId: 'bareImageMap', mapId: 'bareImageMap',

@ -7,6 +7,15 @@ const sharp = require('sharp')
const item = module.exports = {} const item = module.exports = {}
item.findAll = async () => {
const query = 'select * from v_item'
debug(query);
const {rows} = await pg.query(query)
return joinjs.map(rows, mappings, 'itemMap', 'item_');
}
item.findById = async (item_uuid) => { item.findById = async (item_uuid) => {
const query = { const query = {
text: 'select * from v_item where item_uuid = $1', text: 'select * from v_item where item_uuid = $1',
@ -37,7 +46,7 @@ item.findBySlug = async (item_slug) => {
item.create = async (name, urlslug, description, price_cents, published) => { item.create = async (name, urlslug, description, price_cents, published) => {
const query = { const query = {
text: 'select * from public.create_item($1::text, $2::citext, $2::text, $4::integer, $5::boolean)', text: 'select * from public.create_item($1::text, $2::citext, $3::text, $4::integer, $5::boolean)',
values: [ values: [
name, name,
urlslug, urlslug,
@ -80,20 +89,20 @@ item.addImage = async (item_uuid, image_buffer, uploader_uuid) => {
return joinjs.map(rows, mappings, 'itemMap', 'item_') return joinjs.map(rows, mappings, 'itemMap', 'item_')
} }
const queries = { const imageSizeQueries = {
'large': 'select * from get_image_large($1)', 'large': 'select * from get_image_large($1)',
'thumb': 'select * from get_image_thumb($1)' 'thumb': 'select * from get_image_thumb($1)'
} }
item.getImage = async (image_uuid, size) => { item.getImage = async (image_uuid, size) => {
if(!profileSizeQueries[size]) if(!imageSizeQueries[size])
throw new Error(`Cannot get unknown image size: "${size}"`) throw new Error(`Cannot get unknown image size: "${size}"`)
const query = { const query = {
text: profileSizeQueries[size], text: imageSizeQueries[size],
values: [image_uuid] values: [image_uuid]
} }
const {rows} = await pg.query(query) const {rows} = await pg.query(query)
return joinjs.map(rows, relationMaps, 'bareImageMap', 'image_')[0]; return joinjs.map(rows, mappings, 'bareImageMap', 'image_')[0];
} }

@ -51,7 +51,7 @@ create table "cart_item" (
cart_item_uuid uuid primary key default uuid_generate_v4(), cart_item_uuid uuid primary key default uuid_generate_v4(),
cart_item_item_uuid uuid not null references "item" (item_uuid), cart_item_item_uuid uuid not null references "item" (item_uuid),
cart_item_cart_uuid uuid not null references "cart" (cart_uuid), cart_item_cart_uuid uuid not null references "cart" (cart_uuid),
cart_item_count integer not null default 1, cart_item_count integer not null default 1 check (cart_item_count > 0),
cart_item_time_added timestamptz not null default now() cart_item_time_added timestamptz not null default now()
); );

@ -123,9 +123,9 @@ create or replace function public.get_image_large(_image_uuid uuid)
as $function$ as $function$
begin begin
return query select return query select
image_uuid, "image".image_uuid,
image_mime_type, "image".image_mime_type,
image_large_file as image_file "image".image_large_file as image_file
from "image" from "image"
where "image".image_uuid = _image_uuid; where "image".image_uuid = _image_uuid;
end; $function$; end; $function$;
@ -136,9 +136,9 @@ create or replace function public.get_image_thumb(_image_uuid uuid)
as $function$ as $function$
begin begin
return query select return query select
image_uuid, "image".image_uuid,
image_mime_type, "image".image_mime_type,
image_large_thumb as image_file "image".image_thumb_file as image_file
from "image" from "image"
where "image".image_uuid = _image_uuid; where "image".image_uuid = _image_uuid;
end; $function$; end; $function$;

@ -1,3 +1,4 @@
require('dotenv').config()
const express = require('express'); const express = require('express');
const next = require('next'); const next = require('next');

Loading…
Cancel
Save