|
9 | 9 | import assert from 'assert-plus';
|
10 | 10 | import {createDocumentLoader} from './documentLoader.js';
|
11 | 11 | import {decodeList} from '@digitalbazaar/vc-status-list';
|
| 12 | +import {logger} from './logger.js'; |
12 | 13 | import {LruCache} from '@digitalbazaar/lru-memoize';
|
13 | 14 |
|
14 | 15 | const {util: {BedrockError}} = bedrock;
|
@@ -104,6 +105,59 @@ export async function get({id} = {}) {
|
104 | 105 | return SLC_CACHE.memoize({key: id, fn});
|
105 | 106 | }
|
106 | 107 |
|
| 108 | +/** |
| 109 | + * Gets the published credential for the given status list credential ID, |
| 110 | + * refreshing it if it has expired or is not found. |
| 111 | + * |
| 112 | + * @param {object} options - The options to use. |
| 113 | + * @param {string} options.id - The ID of the status list credential. |
| 114 | + * @param {object} options.config - The issuer config. |
| 115 | + * |
| 116 | + * @returns {Promise<object>} Resolves to the stored record. |
| 117 | + */ |
| 118 | +export async function getFresh({id, config} = {}) { |
| 119 | + while(true) { |
| 120 | + try { |
| 121 | + const record = await get({id}); |
| 122 | + // treat expired SLC as `NotFoundError`, but allow for auto-refresh |
| 123 | + // below; get `now` as a minute into the future to ensure any |
| 124 | + // refreshed VC is still valid once returned to the client |
| 125 | + const now = new Date(); |
| 126 | + now.setTime(now.getTime() + 1000 * 60); |
| 127 | + // FIXME: support v2 VCs w/`validUntil` |
| 128 | + const validUntil = new Date(record.credential.expirationDate); |
| 129 | + if(now <= validUntil) { |
| 130 | + // SLC not expired |
| 131 | + return {credential: record.credential}; |
| 132 | + } |
| 133 | + throw new BedrockError( |
| 134 | + 'Status list credential not found.', |
| 135 | + 'NotFoundError', { |
| 136 | + statusListCredential: id, |
| 137 | + httpStatusCode: 404, |
| 138 | + public: true |
| 139 | + }); |
| 140 | + } catch(e) { |
| 141 | + if(e.name !== 'NotFoundError') { |
| 142 | + // unrecoverable error |
| 143 | + throw e; |
| 144 | + } |
| 145 | + try { |
| 146 | + // SLC not found, perhaps expired or not published; try |
| 147 | + // auto-refresh |
| 148 | + console.log('***********AUTO REFRESH***********'); |
| 149 | + const doc = await refresh({id, config}); |
| 150 | + return {credential: doc.content}; |
| 151 | + } catch(refreshError) { |
| 152 | + // log refresh error |
| 153 | + logger.error(refreshError.message, {error: refreshError}); |
| 154 | + // if refresh fails, throw original `NotFoundError` |
| 155 | + throw e; |
| 156 | + } |
| 157 | + } |
| 158 | + } |
| 159 | +} |
| 160 | + |
107 | 161 | /**
|
108 | 162 | * Returns true if a status list credential has been stored and false if not.
|
109 | 163 | *
|
@@ -135,7 +189,6 @@ export async function refresh({id, config} = {}) {
|
135 | 189 | assert.string(id, 'id');
|
136 | 190 | assert.object(config, 'config');
|
137 | 191 |
|
138 |
| - // FIXME: pass `documentStore` instead? |
139 | 192 | const [documentLoader, documentStore] = await Promise.all([
|
140 | 193 | createDocumentLoader({config}),
|
141 | 194 | getDocumentStore({config})
|
|
0 commit comments