📄 LibraryRegistryStorage.sol
D-OPEN SOVEREIGN
1/*
2 * Copyright (c) 2026 DataPond D-Library Pty Ltd. ("The Sanctuary")
3 * Non-Profit Public Library — The World Library
4 *
5 * D-SAFE Certification issued by POND Enterprise.
6 * Published under the D-Open Code Sovereign Licence v1.0 framework
7 * DataPond D-Library All exclusive Right reserved on modifiying the code - CC Attribution to data pond, Non modifiable, Non Commercial.
8 * https://registry.world.bibliotech.com/licence
9 * Source code donated to DataPond D-Library by it's author: data pond.
10 *
11 * Technical Guardian ("The Shield"): Pond Enterprise Pty Ltd. (ACN 694 747 987)
12 * All liability claims about D-SAFE certification issues coming from the published D-Safe direction must be addressed with Pond Enterprise Pty Ltd.
13 * Code Modification automatically voids the certified liability protection provided by Pond Enterprise.
14 */
15
16// SPDX-License-Identifier: Apache-2.0
17pragma solidity ^0.8.24;
18
19import {ErrorLibrary} from "./ErrorLibrary.sol";
20
21interface IPublicLibraryStorage {
22    function libraryExists(uint256 id) external view returns (bool);
23}
24
25contract LibraryRegistryStorage {
26
27    enum LibraryType {
28        Online,
29        PhysicalWithDigitalCopies
30    }
31
32    enum ImportStatus {
33        Pending,
34        InProgress,
35        Completed,
36        Failed
37    }
38
39    struct Collection {
40        string id;
41        string slug;
42        string manifestUrl;
43        string importerUsername;
44        address owner;
45        bool verified;
46        uint256 fileCount;
47        LibraryType libraryType;
48        uint256 publicLibraryId; // 0 = not linked to a PublicLibraryStorage entry
49        string language;
50        bool isPublic;
51        uint64 createdAt;
52        uint64 updatedAt;
53        uint256 initialBookCount;
54        uint256 dSafeBookCount;
55        ImportStatus importStatus;
56        bool published;
57    }
58
59    struct CollectionInput {
60        string id;
61        string slug;
62        string manifestUrl;
63        string importerUsername;
64        address owner;
65        uint256 fileCount;
66        LibraryType libraryType;
67        uint256 publicLibraryId; // 0 = not linked to a PublicLibraryStorage entry
68        string language;
69        bool isPublic;
70        uint256 initialBookCount;
71        uint256 dSafeBookCount;
72    }
73
74    // State Variables
75    address public admin; // single admin or multi-sig if you choose
76    mapping(address => bool) public _isAuthorized; // reserved for future write access (unused for now)
77    IPublicLibraryStorage public publicLibraryStorage;
78
79    // Collection Storage
80    mapping(string => Collection) private collections;
81
82    string[] private unverifiedCollectionIds;
83    string[] private verifiedCollectionIds;
84
85    event AuthorizedAdded(address indexed caller);
86    event AuthorizedRemoved(address indexed caller);
87
88    // Modifiers
89    modifier onlyAdmin() {
90        require(msg.sender == admin, "not admin");
91        _;
92    }
93
94    modifier onlyAuthorized() {
95        require(_isAuthorized[msg.sender], "not authorized");
96        _;
97    }
98
99    constructor() {
100        admin = msg.sender;
101    }
102
103    // ===== Admin / authorization (reserved for future write access) =====
104    function isAuthorized(address caller) public view returns (bool) {
105        require(caller != address(0), "zero addr");
106        return _isAuthorized[caller];
107    }
108
109    function addAuthorized(address caller) public onlyAdmin {
110        require(caller != address(0), "zero addr");
111        _isAuthorized[caller] = true;
112        emit AuthorizedAdded(caller);
113    }
114
115    function removeAuthorized(address caller) public onlyAdmin {
116        _isAuthorized[caller] = false;
117        emit AuthorizedRemoved(caller);
118    }
119
120    function setPublicLibraryStorage(address storageAddr) external onlyAdmin {
121        if (storageAddr == address(0)) revert ErrorLibrary.EmptyAddress();
122        publicLibraryStorage = IPublicLibraryStorage(storageAddr);
123    }
124
125    // 0 means "not linked" and is always allowed; a non-zero id must exist in PublicLibraryStorage.
126    function _validatePublicLibraryId(uint256 publicLibraryId) internal view {
127        if (publicLibraryId == 0) return;
128        if (address(publicLibraryStorage) == address(0) || !publicLibraryStorage.libraryExists(publicLibraryId)) {
129            revert ErrorLibrary.InvalidPublicLibraryId();
130        }
131    }
132
133    // Public Submission — admin-only for now (Called by Orchestrator on Arweave Upload Success)
134    function addUnverifiedCollection(CollectionInput calldata input) external onlyAdmin {
135        if (bytes(collections[input.id].id).length != 0) {
136            revert ErrorLibrary.ErrAlreadyRegistered();
137        }
138        _validatePublicLibraryId(input.publicLibraryId);
139
140        uint64 nowTs = uint64(block.timestamp);
141
142        collections[input.id] = Collection({
143            id: input.id,
144            slug: input.slug,
145            manifestUrl: input.manifestUrl,
146            importerUsername: input.importerUsername,
147            owner: input.owner,
148            verified: false,
149            fileCount: input.fileCount,
150            libraryType: input.libraryType,
151            publicLibraryId: input.publicLibraryId,
152            language: input.language,
153            isPublic: input.isPublic,
154            createdAt: nowTs,
155            updatedAt: nowTs,
156            initialBookCount: input.initialBookCount,
157            dSafeBookCount: input.dSafeBookCount,
158            importStatus: ImportStatus.Pending,
159            published: false
160        });
161
162        unverifiedCollectionIds.push(input.id);
163    }
164
165    // Admin-only Read Operations (Access Control Restricted)
166    function getUnverifiedCollectionIds() external view onlyAdmin returns (string[] memory) {
167        return unverifiedCollectionIds;
168    }
169
170    // Public Read Operations
171    function getVerifiedCollectionIds() external view returns (string[] memory) {
172        return verifiedCollectionIds;
173    }
174
175    function getCollection(string calldata _id) external view returns (Collection memory) {
176        Collection memory c = collections[_id];
177        // Enforce that unverified collections are only viewable by the admin or the owner
178        if (!c.verified && msg.sender != admin && c.owner != msg.sender) {
179            revert ErrorLibrary.Forbidden(msg.sender);
180        }
181        return c;
182    }
183
184    // Manual Verification Workflow (admin-only for now)
185    function verifyCollection(string calldata _id) external onlyAdmin {
186        Collection storage c = collections[_id];
187        if (bytes(c.id).length == 0) revert ErrorLibrary.InvalidProjectId();
188        if (c.verified) revert ErrorLibrary.ErrAlreadyRegistered();
189
190        c.verified = true;
191        c.updatedAt = uint64(block.timestamp);
192        verifiedCollectionIds.push(_id);
193
194        // Remove from unverified list
195        for (uint256 i = 0; i < unverifiedCollectionIds.length; i++) {
196            if (keccak256(bytes(unverifiedCollectionIds[i])) == keccak256(bytes(_id))) {
197                unverifiedCollectionIds[i] = unverifiedCollectionIds[unverifiedCollectionIds.length - 1];
198                unverifiedCollectionIds.pop();
199                break;
200            }
201        }
202    }
203
204    function rejectCollection(string calldata _id) external onlyAdmin {
205        Collection memory c = collections[_id];
206        if (bytes(c.id).length == 0) revert ErrorLibrary.InvalidProjectId();
207        if (c.verified) revert ErrorLibrary.ErrAlreadyRegistered();
208
209        delete collections[_id];
210
211        // Remove from unverified list
212        for (uint256 i = 0; i < unverifiedCollectionIds.length; i++) {
213            if (keccak256(bytes(unverifiedCollectionIds[i])) == keccak256(bytes(_id))) {
214                unverifiedCollectionIds[i] = unverifiedCollectionIds[unverifiedCollectionIds.length - 1];
215                unverifiedCollectionIds.pop();
216                break;
217            }
218        }
219    }
220
221    // ===== Lifecycle updates (admin-only for now) =====
222    function updateCollectionStats(
223        string calldata _id,
224        uint256 initialBookCount,
225        uint256 dSafeBookCount
226    ) external onlyAdmin {
227        Collection storage c = collections[_id];
228        if (bytes(c.id).length == 0) revert ErrorLibrary.InvalidProjectId();
229
230        c.initialBookCount = initialBookCount;
231        c.dSafeBookCount = dSafeBookCount;
232        c.updatedAt = uint64(block.timestamp);
233    }
234
235    function setImportStatus(string calldata _id, ImportStatus status) external onlyAdmin {
236        Collection storage c = collections[_id];
237        if (bytes(c.id).length == 0) revert ErrorLibrary.InvalidProjectId();
238
239        c.importStatus = status;
240        c.updatedAt = uint64(block.timestamp);
241    }
242
243    function setPublished(string calldata _id, bool isPublished) external onlyAdmin {
244        Collection storage c = collections[_id];
245        if (bytes(c.id).length == 0) revert ErrorLibrary.InvalidProjectId();
246
247        c.published = isPublished;
248        c.updatedAt = uint64(block.timestamp);
249    }
250
251    function setCollectionPublicLibrary(string calldata _id, uint256 publicLibraryId) external onlyAdmin {
252        Collection storage c = collections[_id];
253        if (bytes(c.id).length == 0) revert ErrorLibrary.InvalidProjectId();
254        _validatePublicLibraryId(publicLibraryId);
255
256        c.publicLibraryId = publicLibraryId;
257        c.updatedAt = uint64(block.timestamp);
258    }
259}
260