-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat(web): upload images * feat(web): create items * feat(functions): firebase init * feat(functions): generate thumbnail * feat(functions): read exif * feat(functions): set item date by functions * feat(functions): get date from EXIF * feat(functions): add gearId to item * feat(functions): subscribe items
- Loading branch information
1 parent
5e454e8
commit d2d6703
Showing
44 changed files
with
1,912 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,20 @@ | ||
{ | ||
"firestore": { "indexes": "firestore.indexes.json", "rules": "firestore.rules" }, | ||
"functions": [ | ||
{ | ||
"source": "functions", | ||
"codebase": "default", | ||
"ignore": ["node_modules", ".git", "firebase-debug.log", "firebase-debug.*.log"], | ||
"predeploy": [ | ||
"npm --prefix \"$RESOURCE_DIR\" run eslint", | ||
"npm --prefix \"$RESOURCE_DIR\" run build" | ||
] | ||
} | ||
], | ||
"hosting": { | ||
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"], | ||
"public": "web/build", | ||
"rewrites": [{ "source": "**", "destination": "/index.html" }] | ||
} | ||
}, | ||
"storage": { "rules": "storage.rules" } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"env": { "es2021": true, "node": true }, | ||
"extends": ["airbnb-base", "prettier"], | ||
"parser": "@typescript-eslint/parser", | ||
"parserOptions": { "ecmaVersion": "latest", "sourceType": "module" }, | ||
"plugins": ["@typescript-eslint"], | ||
"rules": { | ||
"import/extensions": ["error", "ignorePackages", { "ts": "never" }], | ||
"import/prefer-default-export": "off" | ||
}, | ||
"settings": { "import/resolver": { "node": { "extensions": [".ts"] } } } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Compiled JavaScript files | ||
lib/**/*.js | ||
lib/**/*.js.map | ||
|
||
# TypeScript v1 declaration files | ||
typings/ | ||
|
||
# Node.js dependency directory | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
{ | ||
"name": "functions", | ||
"version": "0.4.0", | ||
"private": true, | ||
"main": "lib/index.js", | ||
"scripts": { | ||
"build": "tsc", | ||
"build:dev": "yarn build", | ||
"build:prd": "yarn build", | ||
"build:stg": "yarn build", | ||
"build:watch": "tsc --watch", | ||
"deploy:dev": "firebase use default && firebase deploy --only functions", | ||
"deploy:prd": "firebase use production && firebase deploy --only functions && firebase use default", | ||
"deploy:stg": "firebase use staging && firebase deploy --only functions && firebase use default", | ||
"eslint": "eslint src/**/*.ts", | ||
"logs": "firebase functions:log", | ||
"serve": "npm run build && firebase emulators:start --only functions", | ||
"shell": "npm run build && firebase functions:shell", | ||
"start": "npm run shell" | ||
}, | ||
"dependencies": { | ||
"date-fns-tz": "^1.3.7", | ||
"exif-reader": "^1.0.3", | ||
"firebase-admin": "^10.0.2", | ||
"firebase-functions": "^3.18.0", | ||
"mkdirp": "^1.0.4", | ||
"sharp": "^0.31.2" | ||
}, | ||
"devDependencies": { | ||
"@types/exif-reader": "^1.0.0", | ||
"@types/mkdirp": "^1.0.2", | ||
"@types/sharp": "^0.31.0", | ||
"@typescript-eslint/eslint-plugin": "^5.42.0", | ||
"@typescript-eslint/parser": "^5.42.0", | ||
"eslint": "^8.9.0", | ||
"eslint-config-airbnb-base": "^15.0.0", | ||
"eslint-config-prettier": "^8.5.0", | ||
"eslint-plugin-import": "^2.26.0", | ||
"firebase-functions-test": "^0.2.0", | ||
"typescript": "^4.5.4" | ||
}, | ||
"engines": { | ||
"node": "16" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import * as admin from 'firebase-admin'; | ||
|
||
admin.initializeApp(); | ||
|
||
export { processUploadedMedia } from './storage/media/onFinalize'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import * as fs from 'fs'; | ||
import * as os from 'os'; | ||
import * as path from 'path'; | ||
|
||
import { firestore, storage } from 'firebase-admin'; | ||
import { logger, region } from 'firebase-functions'; | ||
|
||
import { zonedTimeToUtc } from 'date-fns-tz'; | ||
import exifReader from 'exif-reader'; | ||
import mkdirp from 'mkdirp'; | ||
import sharp from 'sharp'; | ||
|
||
const { increment, serverTimestamp } = firestore.FieldValue; | ||
const getZonedTime = (date?: any) => { | ||
if (!date) return new Date(0); | ||
return zonedTimeToUtc(date, 'Asia/Tokyo'); | ||
}; | ||
|
||
export const processUploadedMedia = region('asia-northeast1') | ||
.storage.object() | ||
.onFinalize(async (object) => { | ||
const { contentType, name } = object; | ||
|
||
if (!contentType) return logger.log('File has no Content-Type.'); | ||
if (!contentType.startsWith('image/')) return logger.log('This is not an image.'); | ||
if (!name) return logger.log('File has no file name.'); | ||
const fileName = path.basename(name).split('.')[0]; | ||
if (fileName === 'thumbnail') return logger.log('Already a Thumbnail.'); | ||
|
||
// Download original file from bucket. | ||
const localFile = path.join(os.tmpdir(), name); | ||
await mkdirp(path.dirname(localFile)); | ||
const bucket = storage().bucket(); | ||
await bucket.file(name).download({ destination: localFile }); | ||
|
||
// Read exif from original file | ||
const exif = await sharp(localFile) | ||
.metadata() | ||
.then((metadata) => metadata.exif && exifReader(metadata.exif)); | ||
|
||
// Generate a thumbnail using sharp. | ||
const destinationFileName = 'thumbnail.jpg'; | ||
const destination = path.normalize(path.join(path.dirname(name), destinationFileName)); | ||
const localThumbnail = path.join(os.tmpdir(), destination); | ||
await sharp(localFile) | ||
.resize(1000) | ||
.toFormat('jpeg') | ||
.toFile(localThumbnail) | ||
.catch((error) => { | ||
logger.error('Error occurred while processing thumbnail'); | ||
throw new Error(error); | ||
}); | ||
|
||
// Upload the thumbnail. | ||
await bucket | ||
.upload(localThumbnail, { destination }) | ||
.then(() => logger.info('Thumbnail uploaded.')) | ||
.catch((error) => { | ||
logger.error('Error occurred while uploading thumbnail'); | ||
throw new Error(error); | ||
}); | ||
|
||
// Once the image has been uploaded delete the local files to free up disk space. | ||
fs.unlinkSync(localFile); | ||
fs.unlinkSync(localThumbnail); | ||
|
||
// Register gear if needed | ||
const gearName = exif?.image?.Model; | ||
if (!gearName) return logger.log('No model name.'); | ||
const gearsRef = firestore().collection('gears'); | ||
const gear = await gearsRef | ||
.where('model', '==', gearName) | ||
.limit(1) | ||
.get() | ||
.then((querySnapshot) => { | ||
if (querySnapshot.empty) return null; | ||
return querySnapshot.docs[0]; | ||
}); | ||
let gearId = gear?.id; | ||
if (gear) { | ||
await gear.ref | ||
.set({ items: increment(1), updatedAt: serverTimestamp() }, { merge: true }) | ||
.then(() => logger.info('Gear items has been incremented.')) | ||
.catch((error) => { | ||
logger.error('Error occurred while updating gear'); | ||
throw new Error(error); | ||
}); | ||
} else { | ||
await gearsRef | ||
.add({ | ||
items: increment(1), | ||
maker: exif.image?.Make || null, | ||
model: gearName, | ||
name: gearName, | ||
type: 'photo', | ||
createdAt: serverTimestamp(), | ||
updatedAt: serverTimestamp(), | ||
}) | ||
.then((ref) => { | ||
logger.info('New gear has been added.'); | ||
gearId = ref.id; | ||
}) | ||
.catch((error) => { | ||
logger.error('Error occurred while creating gear'); | ||
throw new Error(error); | ||
}); | ||
} | ||
|
||
// Update item | ||
const mediaPath = path.dirname(name); | ||
const itemPath = mediaPath.replace('media/', 'items/'); | ||
const itemRef = firestore().doc(itemPath); | ||
return itemRef | ||
.set( | ||
{ | ||
date: getZonedTime(exif?.exif?.DateTimeOriginal), | ||
gearId, | ||
medium: { | ||
exif: { | ||
...exif, | ||
exif: { | ||
...exif?.exif, | ||
DateTimeDigitized: getZonedTime(exif?.exif?.DateTimeDigitized), | ||
DateTimeOriginal: getZonedTime(exif?.exif?.DateTimeOriginal), | ||
}, | ||
image: { | ||
...exif?.image, | ||
ModifyDate: getZonedTime(exif?.image?.ModifyDate), | ||
}, | ||
thumbnail: { | ||
...exif?.thumbnail, | ||
ModifyDate: getZonedTime(exif?.thumbnail?.ModifyDate), | ||
}, | ||
}, | ||
thumbnail: `${mediaPath}/${destinationFileName}`, | ||
}, | ||
updatedAt: serverTimestamp(), | ||
}, | ||
{ merge: true } | ||
) | ||
.then(() => logger.info('Item information updated.')) | ||
.catch((error) => { | ||
logger.error('Error occurred while updating thumbnail path'); | ||
throw new Error(error); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"include": [".eslintrc.json"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"compilerOptions": { | ||
"esModuleInterop": true, | ||
"module": "commonjs", | ||
"noImplicitReturns": true, | ||
"noUnusedLocals": true, | ||
"outDir": "lib", | ||
"sourceMap": true, | ||
"strict": true, | ||
"target": "es2017" | ||
}, | ||
"compileOnSave": true, | ||
"include": ["src"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
rules_version = '2'; | ||
|
||
service firebase.storage { | ||
match /b/{bucket}/o { | ||
function authenticated() { | ||
return request.auth != null; | ||
} | ||
|
||
match /media/{mediumId}/{fileName} { | ||
allow read, write: if authenticated(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.