u
This commit is contained in:
21
README.md
21
README.md
@@ -156,7 +156,7 @@ Backend mapping:
|
|||||||
sort,
|
sort,
|
||||||
full: { w, h, size }, // AVIF, max 1000×1000 (stored as-is when already AVIF and within bounds)
|
full: { w, h, size }, // AVIF, max 1000×1000 (stored as-is when already AVIF and within bounds)
|
||||||
thumb: { w, h }, // scaled AVIF, max 200×200 (fit inside)
|
thumb: { w, h }, // scaled AVIF, max 200×200 (fit inside)
|
||||||
hash, // SHA-256 hex of full image bytes
|
fullHash, // SHA-256 hex — use for GET /api/image/:fullHash and GET /api/thumb/:fullHash
|
||||||
thumbHash, // base64 thumbhash of thumb preview
|
thumbHash, // base64 thumbhash of thumb preview
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -190,20 +190,25 @@ Auth via the article's `categoryarticle` category + `resolveCategoryAccess`. Err
|
|||||||
|
|
||||||
## `pic`
|
## `pic`
|
||||||
|
|
||||||
Lookup by `pic.hash` (SHA-256 hex from `addImage`), not `thumbhash`.
|
Lookup by `pic.hash` (`fullHash` — SHA-256 hex from `addImage.fullHash`), not `thumbhash`.
|
||||||
|
|
||||||
| Method | Signature | Returns |
|
| Method | Signature | Returns |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `getImageByHash` | `(googleAuthId, hash)` | `{ buffer, w, h, size, contentType }` |
|
| `getImageByHash` | `(googleAuthId, fullHash)` | `{ buffer, w, h, size, contentType }` |
|
||||||
| `getThumbByHash` | `(googleAuthId, hash)` | `{ buffer, w, h, contentType }` |
|
| `getThumbByHash` | `(googleAuthId, fullHash)` | `{ buffer, w, h, contentType }` |
|
||||||
|
|
||||||
Auth via `articlepic` → `categoryarticle` → `resolveCategoryAccess`. Errors: `Image not found`, `Not authorized`.
|
Auth via `articlepic` → `categoryarticle` → `resolveCategoryAccess`. Errors: `Image not found`, `Not authorized`.
|
||||||
|
|
||||||
`contentType` is always `image/avif`. In dashback:
|
`contentType` is always `image/avif`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const { buffer, contentType } = await store.pic.getImageByHash(req.user.id, hash);
|
// GET /api/image/:fullHash
|
||||||
|
const { buffer, contentType } = await store.pic.getImageByHash(req.user.id, fullHash);
|
||||||
res.set('Content-Type', contentType).send(buffer);
|
res.set('Content-Type', contentType).send(buffer);
|
||||||
|
|
||||||
|
// GET /api/thumb/:fullHash
|
||||||
|
const thumb = await store.pic.getThumbByHash(req.user.id, fullHash);
|
||||||
|
res.set('Content-Type', thumb.contentType).send(thumb.buffer);
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -224,8 +229,8 @@ res.set('Content-Type', contentType).send(buffer);
|
|||||||
| `GET /api/article/:sku` | `article.getBySku(googleAuthId, sku)` |
|
| `GET /api/article/:sku` | `article.getBySku(googleAuthId, sku)` |
|
||||||
| `PATCH /api/article/:sku` | `article.updateBySku(googleAuthId, sku, { name })` |
|
| `PATCH /api/article/:sku` | `article.updateBySku(googleAuthId, sku, { name })` |
|
||||||
| `POST /api/article/:sku/image` | `article.addImage(googleAuthId, sku, imageBuffer)` |
|
| `POST /api/article/:sku/image` | `article.addImage(googleAuthId, sku, imageBuffer)` |
|
||||||
| `GET /api/image/:hash` | `pic.getImageByHash(googleAuthId, hash)` |
|
| `GET /api/image/:fullHash` | `pic.getImageByHash(googleAuthId, fullHash)` |
|
||||||
| `GET /api/image/:hash/thumb` | `pic.getThumbByHash(googleAuthId, hash)` |
|
| `GET /api/thumb/:fullHash` | `pic.getThumbByHash(googleAuthId, fullHash)` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -133,12 +133,12 @@ const MENU = {
|
|||||||
{
|
{
|
||||||
value: 'pic.getImageByHash',
|
value: 'pic.getImageByHash',
|
||||||
label: 'getImageByHash',
|
label: 'getImageByHash',
|
||||||
hint: 'googleAuthId, hash → meta',
|
hint: 'googleAuthId, fullHash → meta',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'pic.getThumbByHash',
|
value: 'pic.getThumbByHash',
|
||||||
label: 'getThumbByHash',
|
label: 'getThumbByHash',
|
||||||
hint: 'googleAuthId, hash → meta',
|
hint: 'googleAuthId, fullHash → meta',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -461,11 +461,14 @@ async function runMethod(client, action) {
|
|||||||
case 'pic.getImageByHash': {
|
case 'pic.getImageByHash': {
|
||||||
const googleAuthId = await askInt('google_auth id');
|
const googleAuthId = await askInt('google_auth id');
|
||||||
if (googleAuthId == null) return;
|
if (googleAuthId == null) return;
|
||||||
const hash = await askText('hash (SHA-256 hex)', {
|
const fullHash = await askText('fullHash (SHA-256 hex)', {
|
||||||
validate: (v) => (!v?.trim() ? 'Required' : undefined),
|
validate: (v) => (!v?.trim() ? 'Required' : undefined),
|
||||||
});
|
});
|
||||||
if (hash == null) return;
|
if (fullHash == null) return;
|
||||||
const image = await client.pic.getImageByHash(Number(googleAuthId), hash.trim());
|
const image = await client.pic.getImageByHash(
|
||||||
|
Number(googleAuthId),
|
||||||
|
fullHash.trim(),
|
||||||
|
);
|
||||||
return [
|
return [
|
||||||
'result',
|
'result',
|
||||||
{
|
{
|
||||||
@@ -480,11 +483,14 @@ async function runMethod(client, action) {
|
|||||||
case 'pic.getThumbByHash': {
|
case 'pic.getThumbByHash': {
|
||||||
const googleAuthId = await askInt('google_auth id');
|
const googleAuthId = await askInt('google_auth id');
|
||||||
if (googleAuthId == null) return;
|
if (googleAuthId == null) return;
|
||||||
const hash = await askText('hash (SHA-256 hex)', {
|
const fullHash = await askText('fullHash (SHA-256 hex)', {
|
||||||
validate: (v) => (!v?.trim() ? 'Required' : undefined),
|
validate: (v) => (!v?.trim() ? 'Required' : undefined),
|
||||||
});
|
});
|
||||||
if (hash == null) return;
|
if (fullHash == null) return;
|
||||||
const image = await client.pic.getThumbByHash(Number(googleAuthId), hash.trim());
|
const image = await client.pic.getThumbByHash(
|
||||||
|
Number(googleAuthId),
|
||||||
|
fullHash.trim(),
|
||||||
|
);
|
||||||
return [
|
return [
|
||||||
'result',
|
'result',
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export default function addImage(pool) {
|
|||||||
w: processed.thumb.w,
|
w: processed.thumb.w,
|
||||||
h: processed.thumb.h,
|
h: processed.thumb.h,
|
||||||
},
|
},
|
||||||
hash: processed.full.hash,
|
fullHash: processed.full.hash,
|
||||||
thumbHash: processed.thumb.hash,
|
thumbHash: processed.thumb.hash,
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import resolvePicAccess from './resolvePicAccess.js';
|
|||||||
export default function getImageByHash(pool) {
|
export default function getImageByHash(pool) {
|
||||||
const authorize = resolvePicAccess(pool);
|
const authorize = resolvePicAccess(pool);
|
||||||
|
|
||||||
return async (googleAuthId, hash) => {
|
return async (googleAuthId, fullHash) => {
|
||||||
await authorize(googleAuthId, hash);
|
await authorize(googleAuthId, fullHash);
|
||||||
|
|
||||||
const result = await pool
|
const result = await pool
|
||||||
.request()
|
.request()
|
||||||
.input('hash', hash)
|
.input('hash', fullHash)
|
||||||
.query(GET_FULL_BY_HASH);
|
.query(GET_FULL_BY_HASH);
|
||||||
|
|
||||||
const row = result.recordset[0];
|
const row = result.recordset[0];
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import resolvePicAccess from './resolvePicAccess.js';
|
|||||||
export default function getThumbByHash(pool) {
|
export default function getThumbByHash(pool) {
|
||||||
const authorize = resolvePicAccess(pool);
|
const authorize = resolvePicAccess(pool);
|
||||||
|
|
||||||
return async (googleAuthId, hash) => {
|
return async (googleAuthId, fullHash) => {
|
||||||
await authorize(googleAuthId, hash);
|
await authorize(googleAuthId, fullHash);
|
||||||
|
|
||||||
const result = await pool
|
const result = await pool
|
||||||
.request()
|
.request()
|
||||||
.input('hash', hash)
|
.input('hash', fullHash)
|
||||||
.query(GET_THUMB_BY_HASH);
|
.query(GET_THUMB_BY_HASH);
|
||||||
|
|
||||||
const row = result.recordset[0];
|
const row = result.recordset[0];
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import resolveCategoryAccess from '../category/resolveCategoryAccess.js';
|
|||||||
export default function resolvePicAccess(pool) {
|
export default function resolvePicAccess(pool) {
|
||||||
const access = resolveCategoryAccess(pool);
|
const access = resolveCategoryAccess(pool);
|
||||||
|
|
||||||
return async (googleAuthId, hash) => {
|
return async (googleAuthId, fullHash) => {
|
||||||
const result = await pool
|
const result = await pool
|
||||||
.request()
|
.request()
|
||||||
.input('hash', hash)
|
.input('hash', fullHash)
|
||||||
.query(GET_CATEGORY_ID_BY_HASH);
|
.query(GET_CATEGORY_ID_BY_HASH);
|
||||||
|
|
||||||
const categoryId = result.recordset[0]?.categoryId;
|
const categoryId = result.recordset[0]?.categoryId;
|
||||||
|
|||||||
Reference in New Issue
Block a user