├── .github └── workflows │ ├── build.yml │ ├── postgres-build.yml │ ├── sbt-dependency-graph.yaml │ └── snyk.yml ├── .gitignore ├── .java-version ├── .nvmrc ├── .tool-versions ├── Brewfile ├── Brewfile.lock.json ├── CODEOWNERS ├── LICENSE ├── README.md ├── backend ├── app │ ├── AppComponents.scala │ ├── AppLoader.scala │ ├── commands │ │ ├── Command.scala │ │ ├── CreateCollection.scala │ │ ├── CreateIngestion.scala │ │ ├── DeleteResource.scala │ │ ├── GetBlobObjectData.scala │ │ ├── GetEmailThread.scala │ │ ├── GetFilters.scala │ │ ├── GetPagePreview.scala │ │ ├── GetPages.scala │ │ ├── GetResource.scala │ │ ├── IngestFile.scala │ │ ├── TFACommands.scala │ │ └── VerifyIngestion.scala │ ├── controllers │ │ ├── api │ │ │ ├── Authentication.scala │ │ │ ├── Blobs.scala │ │ │ ├── Collections.scala │ │ │ ├── Comments.scala │ │ │ ├── Documents.scala │ │ │ ├── Emails.scala │ │ │ ├── Events.scala │ │ │ ├── Filters.scala │ │ │ ├── Ingestion.scala │ │ │ ├── IngestionEvents.scala │ │ │ ├── MimeTypes.scala │ │ │ ├── PagesController.scala │ │ │ ├── Previews.scala │ │ │ ├── Resource.scala │ │ │ ├── Search.scala │ │ │ ├── Users.scala │ │ │ └── Workspaces.scala │ │ ├── frontend │ │ │ └── App.scala │ │ └── genesis │ │ │ └── Genesis.scala │ ├── extraction │ │ ├── DocumentBodyExtractor.scala │ │ ├── ExternalExtractor.scala │ │ ├── ExternalTranscriptionExtractor.scala │ │ ├── ExternalTranscriptionWorker.scala │ │ ├── Extractor.scala │ │ ├── FileExtractor.scala │ │ ├── MetadataEnrichment.scala │ │ ├── MimeTypeMapper.scala │ │ ├── TranscriptionExtractor.scala │ │ ├── Worker.scala │ │ ├── archives │ │ │ ├── RarExtractor.scala │ │ │ └── ZipExtractor.scala │ │ ├── email │ │ │ ├── CustomTikaDetector.scala │ │ │ ├── EmailContentTypeCleaner.java │ │ │ ├── JakartaMail.scala │ │ │ ├── eml │ │ │ │ ├── EmlEmailExtractor.scala │ │ │ │ └── EmlParser.scala │ │ │ ├── mbox │ │ │ │ ├── MBoxEmailDetector.scala │ │ │ │ └── MBoxEmailExtractor.scala │ │ │ ├── msg │ │ │ │ └── MsgEmailExtractor.scala │ │ │ ├── olm │ │ │ │ ├── OlmEmailDetector.scala │ │ │ │ ├── OlmEmailExtractor.scala │ │ │ │ └── OlmMessage.scala │ │ │ └── pst │ │ │ │ ├── PstEmailExtractor.scala │ │ │ │ └── iterators │ │ │ │ └── PSTIterator.scala │ │ ├── ocr │ │ │ ├── BaseOcrExtractor.scala │ │ │ ├── ImageOcrExtractor.scala │ │ │ ├── OcrMyPdfExtractor.scala │ │ │ ├── OcrMyPdfImageExtractor.scala │ │ │ └── TesseractPdfOcrExtractor.scala │ │ └── tables │ │ │ ├── CsvTableExtractor.scala │ │ │ └── ExcelTableExtractor.scala │ ├── ingestion │ │ ├── IngestionContextBuilder.scala │ │ └── phase2 │ │ │ └── IngestStorePolling.scala │ ├── model │ │ ├── Email.scala │ │ ├── ExtractedDateTime.scala │ │ ├── ObjectData.scala │ │ ├── annotations │ │ │ ├── Comment.scala │ │ │ └── Workspace.scala │ │ ├── frontend │ │ │ ├── Chip.scala │ │ │ ├── ClientConfig.scala │ │ │ ├── EmailThread.scala │ │ │ ├── ExtractionFailure.scala │ │ │ ├── Filter.scala │ │ │ ├── Highlight.scala │ │ │ ├── HighlightableText.scala │ │ │ ├── Node.scala │ │ │ ├── Paging.scala │ │ │ ├── Resource.scala │ │ │ ├── SearchResult.scala │ │ │ ├── TotpActivation.scala │ │ │ ├── TreeEntry.scala │ │ │ ├── email │ │ │ │ └── EmailThread.scala │ │ │ └── user │ │ │ │ ├── NewGenesisUser.scala │ │ │ │ ├── PartialUser.scala │ │ │ │ ├── README.md │ │ │ │ └── UserRegistration.scala │ │ ├── index │ │ │ ├── Document.scala │ │ │ ├── Flags.scala │ │ │ ├── IndexedResource.scala │ │ │ ├── Page.scala │ │ │ ├── SearchParameters.scala │ │ │ └── TableRow.scala │ │ ├── ingestion │ │ │ ├── EmailContext.scala │ │ │ ├── FileContext.scala │ │ │ └── OcrMyPdfFlag.scala │ │ ├── manifest │ │ │ ├── Blob.scala │ │ │ ├── Collection.scala │ │ │ ├── Ingestion.scala │ │ │ ├── MimeType.scala │ │ │ ├── UserWithCollections.scala │ │ │ └── WorkItem.scala │ │ ├── package.scala │ │ └── user │ │ │ ├── BCryptPassword.scala │ │ │ ├── DBUser.scala │ │ │ └── UserPermissions.scala │ ├── services │ │ ├── Config.scala │ │ ├── ElasticsearchSyntax.scala │ │ ├── ExternalWorkerScheduler.scala │ │ ├── IngestStorage.scala │ │ ├── MetricsService.scala │ │ ├── ObjectStorage.scala │ │ ├── ScratchSpace.scala │ │ ├── Tika.scala │ │ ├── WorkerScheduler.scala │ │ ├── annotations │ │ │ ├── Annotations.scala │ │ │ └── Neo4jAnnotations.scala │ │ ├── events │ │ │ └── Events.scala │ │ ├── index │ │ │ ├── Aggregations.scala │ │ │ ├── ElasticsearchPages.scala │ │ │ ├── ElasticsearchResources.scala │ │ │ ├── HighlightFields.scala │ │ │ ├── HitReaders.scala │ │ │ ├── Index.scala │ │ │ ├── IngestionData.scala │ │ │ ├── Mappings.scala │ │ │ ├── Pages.scala │ │ │ ├── Pages2.scala │ │ │ └── SearchContext.scala │ │ ├── ingestion │ │ │ └── IngestionServices.scala │ │ ├── manifest │ │ │ ├── Manifest.scala │ │ │ └── Neo4jManifest.scala │ │ ├── observability │ │ │ ├── Models.scala │ │ │ └── PostgresClient.scala │ │ ├── previewing │ │ │ ├── HtmlPreviewGenerator.scala │ │ │ ├── LibreOfficePreviewGenerator.scala │ │ │ ├── PreviewGenerator.scala │ │ │ └── PreviewService.scala │ │ ├── table │ │ │ └── Table.scala │ │ └── users │ │ │ ├── Neo4jUserManagement.scala │ │ │ └── UserManagement.scala │ └── utils │ │ ├── AllowFrameFilter.scala │ │ ├── AwsDiscovery.scala │ │ ├── BasicStdErrLogger.scala │ │ ├── Binders.scala │ │ ├── Chart.scala │ │ ├── DateTimeUtils.scala │ │ ├── EitherTHelper.scala │ │ ├── Epoch.scala │ │ ├── FfMpeg.scala │ │ ├── HtmlToPlainText.scala │ │ ├── MimeDetails.scala │ │ ├── Neo4jHelper.scala │ │ ├── Ocr.scala │ │ ├── PDFUtil.scala │ │ ├── RequestLoggingFilter.scala │ │ ├── Stopwatch.scala │ │ ├── Time.scala │ │ ├── UriCleaner.scala │ │ ├── Whisper.scala │ │ ├── WorkerControl.scala │ │ ├── auth │ │ ├── AuthActionBuilder.scala │ │ ├── PasswordHashing.scala │ │ ├── PasswordValidator.scala │ │ ├── Token.scala │ │ ├── User.scala │ │ ├── package.scala │ │ ├── providers │ │ │ ├── DatabaseUserProvider.scala │ │ │ ├── PanDomainUserProvider.scala │ │ │ └── UserProvider.scala │ │ └── totp │ │ │ ├── Algorithm.scala │ │ │ ├── Secret.scala │ │ │ ├── SecureSecretGenerator.scala │ │ │ ├── TfaToken.scala │ │ │ └── Totp.scala │ │ ├── aws │ │ ├── AwsAsyncHandler.scala │ │ ├── AwsErrors.scala │ │ ├── AwsExamples.scala │ │ ├── CreateBuckets.scala │ │ └── S3Client.scala │ │ └── controller │ │ ├── AuthApiController.scala │ │ ├── AuthControllerComponents.scala │ │ ├── DownloadHelper.scala │ │ ├── FailureToResultMapper.scala │ │ ├── NoAuthApiController.scala │ │ ├── OptionalAuthApiController.scala │ │ └── PfiApiController.scala ├── conf │ ├── application.conf │ ├── logback.xml │ ├── org │ │ └── apache │ │ │ └── tika │ │ │ └── mime │ │ │ └── custom-mimetypes.xml │ └── routes ├── lib │ ├── mbox-2.0.2.jar │ └── msgparser-1.2.0-SNAPSHOT.jar ├── public │ └── third-party │ │ ├── pdf.worker.min.js │ │ └── pdfjs-2.4.456-dist │ │ ├── LICENSE │ │ ├── build │ │ ├── pdf.js │ │ ├── pdf.js.map │ │ ├── pdf.worker.js │ │ └── pdf.worker.js.map │ │ └── web │ │ ├── cmaps │ │ ├── 78-EUC-H.bcmap │ │ ├── 78-EUC-V.bcmap │ │ ├── 78-H.bcmap │ │ ├── 78-RKSJ-H.bcmap │ │ ├── 78-RKSJ-V.bcmap │ │ ├── 78-V.bcmap │ │ ├── 78ms-RKSJ-H.bcmap │ │ ├── 78ms-RKSJ-V.bcmap │ │ ├── 83pv-RKSJ-H.bcmap │ │ ├── 90ms-RKSJ-H.bcmap │ │ ├── 90ms-RKSJ-V.bcmap │ │ ├── 90msp-RKSJ-H.bcmap │ │ ├── 90msp-RKSJ-V.bcmap │ │ ├── 90pv-RKSJ-H.bcmap │ │ ├── 90pv-RKSJ-V.bcmap │ │ ├── Add-H.bcmap │ │ ├── Add-RKSJ-H.bcmap │ │ ├── Add-RKSJ-V.bcmap │ │ ├── Add-V.bcmap │ │ ├── Adobe-CNS1-0.bcmap │ │ ├── Adobe-CNS1-1.bcmap │ │ ├── Adobe-CNS1-2.bcmap │ │ ├── Adobe-CNS1-3.bcmap │ │ ├── Adobe-CNS1-4.bcmap │ │ ├── Adobe-CNS1-5.bcmap │ │ ├── Adobe-CNS1-6.bcmap │ │ ├── Adobe-CNS1-UCS2.bcmap │ │ ├── Adobe-GB1-0.bcmap │ │ ├── Adobe-GB1-1.bcmap │ │ ├── Adobe-GB1-2.bcmap │ │ ├── Adobe-GB1-3.bcmap │ │ ├── Adobe-GB1-4.bcmap │ │ ├── Adobe-GB1-5.bcmap │ │ ├── Adobe-GB1-UCS2.bcmap │ │ ├── Adobe-Japan1-0.bcmap │ │ ├── Adobe-Japan1-1.bcmap │ │ ├── Adobe-Japan1-2.bcmap │ │ ├── Adobe-Japan1-3.bcmap │ │ ├── Adobe-Japan1-4.bcmap │ │ ├── Adobe-Japan1-5.bcmap │ │ ├── Adobe-Japan1-6.bcmap │ │ ├── Adobe-Japan1-UCS2.bcmap │ │ ├── Adobe-Korea1-0.bcmap │ │ ├── Adobe-Korea1-1.bcmap │ │ ├── Adobe-Korea1-2.bcmap │ │ ├── Adobe-Korea1-UCS2.bcmap │ │ ├── B5-H.bcmap │ │ ├── B5-V.bcmap │ │ ├── B5pc-H.bcmap │ │ ├── B5pc-V.bcmap │ │ ├── CNS-EUC-H.bcmap │ │ ├── CNS-EUC-V.bcmap │ │ ├── CNS1-H.bcmap │ │ ├── CNS1-V.bcmap │ │ ├── CNS2-H.bcmap │ │ ├── CNS2-V.bcmap │ │ ├── ETHK-B5-H.bcmap │ │ ├── ETHK-B5-V.bcmap │ │ ├── ETen-B5-H.bcmap │ │ ├── ETen-B5-V.bcmap │ │ ├── ETenms-B5-H.bcmap │ │ ├── ETenms-B5-V.bcmap │ │ ├── EUC-H.bcmap │ │ ├── EUC-V.bcmap │ │ ├── Ext-H.bcmap │ │ ├── Ext-RKSJ-H.bcmap │ │ ├── Ext-RKSJ-V.bcmap │ │ ├── Ext-V.bcmap │ │ ├── GB-EUC-H.bcmap │ │ ├── GB-EUC-V.bcmap │ │ ├── GB-H.bcmap │ │ ├── GB-V.bcmap │ │ ├── GBK-EUC-H.bcmap │ │ ├── GBK-EUC-V.bcmap │ │ ├── GBK2K-H.bcmap │ │ ├── GBK2K-V.bcmap │ │ ├── GBKp-EUC-H.bcmap │ │ ├── GBKp-EUC-V.bcmap │ │ ├── GBT-EUC-H.bcmap │ │ ├── GBT-EUC-V.bcmap │ │ ├── GBT-H.bcmap │ │ ├── GBT-V.bcmap │ │ ├── GBTpc-EUC-H.bcmap │ │ ├── GBTpc-EUC-V.bcmap │ │ ├── GBpc-EUC-H.bcmap │ │ ├── GBpc-EUC-V.bcmap │ │ ├── H.bcmap │ │ ├── HKdla-B5-H.bcmap │ │ ├── HKdla-B5-V.bcmap │ │ ├── HKdlb-B5-H.bcmap │ │ ├── HKdlb-B5-V.bcmap │ │ ├── HKgccs-B5-H.bcmap │ │ ├── HKgccs-B5-V.bcmap │ │ ├── HKm314-B5-H.bcmap │ │ ├── HKm314-B5-V.bcmap │ │ ├── HKm471-B5-H.bcmap │ │ ├── HKm471-B5-V.bcmap │ │ ├── HKscs-B5-H.bcmap │ │ ├── HKscs-B5-V.bcmap │ │ ├── Hankaku.bcmap │ │ ├── Hiragana.bcmap │ │ ├── KSC-EUC-H.bcmap │ │ ├── KSC-EUC-V.bcmap │ │ ├── KSC-H.bcmap │ │ ├── KSC-Johab-H.bcmap │ │ ├── KSC-Johab-V.bcmap │ │ ├── KSC-V.bcmap │ │ ├── KSCms-UHC-H.bcmap │ │ ├── KSCms-UHC-HW-H.bcmap │ │ ├── KSCms-UHC-HW-V.bcmap │ │ ├── KSCms-UHC-V.bcmap │ │ ├── KSCpc-EUC-H.bcmap │ │ ├── KSCpc-EUC-V.bcmap │ │ ├── Katakana.bcmap │ │ ├── LICENSE │ │ ├── NWP-H.bcmap │ │ ├── NWP-V.bcmap │ │ ├── RKSJ-H.bcmap │ │ ├── RKSJ-V.bcmap │ │ ├── Roman.bcmap │ │ ├── UniCNS-UCS2-H.bcmap │ │ ├── UniCNS-UCS2-V.bcmap │ │ ├── UniCNS-UTF16-H.bcmap │ │ ├── UniCNS-UTF16-V.bcmap │ │ ├── UniCNS-UTF32-H.bcmap │ │ ├── UniCNS-UTF32-V.bcmap │ │ ├── UniCNS-UTF8-H.bcmap │ │ ├── UniCNS-UTF8-V.bcmap │ │ ├── UniGB-UCS2-H.bcmap │ │ ├── UniGB-UCS2-V.bcmap │ │ ├── UniGB-UTF16-H.bcmap │ │ ├── UniGB-UTF16-V.bcmap │ │ ├── UniGB-UTF32-H.bcmap │ │ ├── UniGB-UTF32-V.bcmap │ │ ├── UniGB-UTF8-H.bcmap │ │ ├── UniGB-UTF8-V.bcmap │ │ ├── UniJIS-UCS2-H.bcmap │ │ ├── UniJIS-UCS2-HW-H.bcmap │ │ ├── UniJIS-UCS2-HW-V.bcmap │ │ ├── UniJIS-UCS2-V.bcmap │ │ ├── UniJIS-UTF16-H.bcmap │ │ ├── UniJIS-UTF16-V.bcmap │ │ ├── UniJIS-UTF32-H.bcmap │ │ ├── UniJIS-UTF32-V.bcmap │ │ ├── UniJIS-UTF8-H.bcmap │ │ ├── UniJIS-UTF8-V.bcmap │ │ ├── UniJIS2004-UTF16-H.bcmap │ │ ├── UniJIS2004-UTF16-V.bcmap │ │ ├── UniJIS2004-UTF32-H.bcmap │ │ ├── UniJIS2004-UTF32-V.bcmap │ │ ├── UniJIS2004-UTF8-H.bcmap │ │ ├── UniJIS2004-UTF8-V.bcmap │ │ ├── UniJISPro-UCS2-HW-V.bcmap │ │ ├── UniJISPro-UCS2-V.bcmap │ │ ├── UniJISPro-UTF8-V.bcmap │ │ ├── UniJISX0213-UTF32-H.bcmap │ │ ├── UniJISX0213-UTF32-V.bcmap │ │ ├── UniJISX02132004-UTF32-H.bcmap │ │ ├── UniJISX02132004-UTF32-V.bcmap │ │ ├── UniKS-UCS2-H.bcmap │ │ ├── UniKS-UCS2-V.bcmap │ │ ├── UniKS-UTF16-H.bcmap │ │ ├── UniKS-UTF16-V.bcmap │ │ ├── UniKS-UTF32-H.bcmap │ │ ├── UniKS-UTF32-V.bcmap │ │ ├── UniKS-UTF8-H.bcmap │ │ ├── UniKS-UTF8-V.bcmap │ │ ├── V.bcmap │ │ └── WP-Symbol.bcmap │ │ ├── compressed.tracemonkey-pldi-09.pdf │ │ ├── debugger.js │ │ ├── images │ │ ├── annotation-check.svg │ │ ├── annotation-comment.svg │ │ ├── annotation-help.svg │ │ ├── annotation-insert.svg │ │ ├── annotation-key.svg │ │ ├── annotation-newparagraph.svg │ │ ├── annotation-noicon.svg │ │ ├── annotation-note.svg │ │ ├── annotation-paragraph.svg │ │ ├── findbarButton-next-rtl.png │ │ ├── findbarButton-next-rtl@2x.png │ │ ├── findbarButton-next.png │ │ ├── findbarButton-next@2x.png │ │ ├── findbarButton-previous-rtl.png │ │ ├── findbarButton-previous-rtl@2x.png │ │ ├── findbarButton-previous.png │ │ ├── findbarButton-previous@2x.png │ │ ├── grab.cur │ │ ├── grabbing.cur │ │ ├── loading-icon.gif │ │ ├── loading-small.png │ │ ├── loading-small@2x.png │ │ ├── secondaryToolbarButton-documentProperties.png │ │ ├── secondaryToolbarButton-documentProperties@2x.png │ │ ├── secondaryToolbarButton-firstPage.png │ │ ├── secondaryToolbarButton-firstPage@2x.png │ │ ├── secondaryToolbarButton-handTool.png │ │ ├── secondaryToolbarButton-handTool@2x.png │ │ ├── secondaryToolbarButton-lastPage.png │ │ ├── secondaryToolbarButton-lastPage@2x.png │ │ ├── secondaryToolbarButton-rotateCcw.png │ │ ├── secondaryToolbarButton-rotateCcw@2x.png │ │ ├── secondaryToolbarButton-rotateCw.png │ │ ├── secondaryToolbarButton-rotateCw@2x.png │ │ ├── secondaryToolbarButton-scrollHorizontal.png │ │ ├── secondaryToolbarButton-scrollHorizontal@2x.png │ │ ├── secondaryToolbarButton-scrollVertical.png │ │ ├── secondaryToolbarButton-scrollVertical@2x.png │ │ ├── secondaryToolbarButton-scrollWrapped.png │ │ ├── secondaryToolbarButton-scrollWrapped@2x.png │ │ ├── secondaryToolbarButton-selectTool.png │ │ ├── secondaryToolbarButton-selectTool@2x.png │ │ ├── secondaryToolbarButton-spreadEven.png │ │ ├── secondaryToolbarButton-spreadEven@2x.png │ │ ├── secondaryToolbarButton-spreadNone.png │ │ ├── secondaryToolbarButton-spreadNone@2x.png │ │ ├── secondaryToolbarButton-spreadOdd.png │ │ ├── secondaryToolbarButton-spreadOdd@2x.png │ │ ├── shadow.png │ │ ├── texture.png │ │ ├── toolbarButton-bookmark.png │ │ ├── toolbarButton-bookmark@2x.png │ │ ├── toolbarButton-download.png │ │ ├── toolbarButton-download@2x.png │ │ ├── toolbarButton-menuArrows.png │ │ ├── toolbarButton-menuArrows@2x.png │ │ ├── toolbarButton-openFile.png │ │ ├── toolbarButton-openFile@2x.png │ │ ├── toolbarButton-pageDown-rtl.png │ │ ├── toolbarButton-pageDown-rtl@2x.png │ │ ├── toolbarButton-pageDown.png │ │ ├── toolbarButton-pageDown@2x.png │ │ ├── toolbarButton-pageUp-rtl.png │ │ ├── toolbarButton-pageUp-rtl@2x.png │ │ ├── toolbarButton-pageUp.png │ │ ├── toolbarButton-pageUp@2x.png │ │ ├── toolbarButton-presentationMode.png │ │ ├── toolbarButton-presentationMode@2x.png │ │ ├── toolbarButton-print.png │ │ ├── toolbarButton-print@2x.png │ │ ├── toolbarButton-search.png │ │ ├── toolbarButton-search@2x.png │ │ ├── toolbarButton-secondaryToolbarToggle-rtl.png │ │ ├── toolbarButton-secondaryToolbarToggle-rtl@2x.png │ │ ├── toolbarButton-secondaryToolbarToggle.png │ │ ├── toolbarButton-secondaryToolbarToggle@2x.png │ │ ├── toolbarButton-sidebarToggle-rtl.png │ │ ├── toolbarButton-sidebarToggle-rtl@2x.png │ │ ├── toolbarButton-sidebarToggle.png │ │ ├── toolbarButton-sidebarToggle@2x.png │ │ ├── toolbarButton-viewAttachments.png │ │ ├── toolbarButton-viewAttachments@2x.png │ │ ├── toolbarButton-viewOutline-rtl.png │ │ ├── toolbarButton-viewOutline-rtl@2x.png │ │ ├── toolbarButton-viewOutline.png │ │ ├── toolbarButton-viewOutline@2x.png │ │ ├── toolbarButton-viewThumbnail.png │ │ ├── toolbarButton-viewThumbnail@2x.png │ │ ├── toolbarButton-zoomIn.png │ │ ├── toolbarButton-zoomIn@2x.png │ │ ├── toolbarButton-zoomOut.png │ │ ├── toolbarButton-zoomOut@2x.png │ │ ├── treeitem-collapsed-rtl.png │ │ ├── treeitem-collapsed-rtl@2x.png │ │ ├── treeitem-collapsed.png │ │ ├── treeitem-collapsed@2x.png │ │ ├── treeitem-expanded.png │ │ └── treeitem-expanded@2x.png │ │ ├── locale │ │ ├── ach │ │ │ └── viewer.properties │ │ ├── af │ │ │ └── viewer.properties │ │ ├── an │ │ │ └── viewer.properties │ │ ├── ar │ │ │ └── viewer.properties │ │ ├── ast │ │ │ └── viewer.properties │ │ ├── az │ │ │ └── viewer.properties │ │ ├── be │ │ │ └── viewer.properties │ │ ├── bg │ │ │ └── viewer.properties │ │ ├── bn │ │ │ └── viewer.properties │ │ ├── bo │ │ │ └── viewer.properties │ │ ├── br │ │ │ └── viewer.properties │ │ ├── brx │ │ │ └── viewer.properties │ │ ├── bs │ │ │ └── viewer.properties │ │ ├── ca │ │ │ └── viewer.properties │ │ ├── cak │ │ │ └── viewer.properties │ │ ├── cs │ │ │ └── viewer.properties │ │ ├── cy │ │ │ └── viewer.properties │ │ ├── da │ │ │ └── viewer.properties │ │ ├── de │ │ │ └── viewer.properties │ │ ├── dsb │ │ │ └── viewer.properties │ │ ├── el │ │ │ └── viewer.properties │ │ ├── en-CA │ │ │ └── viewer.properties │ │ ├── en-GB │ │ │ └── viewer.properties │ │ ├── en-US │ │ │ └── viewer.properties │ │ ├── eo │ │ │ └── viewer.properties │ │ ├── es-AR │ │ │ └── viewer.properties │ │ ├── es-CL │ │ │ └── viewer.properties │ │ ├── es-ES │ │ │ └── viewer.properties │ │ ├── es-MX │ │ │ └── viewer.properties │ │ ├── et │ │ │ └── viewer.properties │ │ ├── eu │ │ │ └── viewer.properties │ │ ├── fa │ │ │ └── viewer.properties │ │ ├── ff │ │ │ └── viewer.properties │ │ ├── fi │ │ │ └── viewer.properties │ │ ├── fr │ │ │ └── viewer.properties │ │ ├── fy-NL │ │ │ └── viewer.properties │ │ ├── ga-IE │ │ │ └── viewer.properties │ │ ├── gd │ │ │ └── viewer.properties │ │ ├── gl │ │ │ └── viewer.properties │ │ ├── gn │ │ │ └── viewer.properties │ │ ├── gu-IN │ │ │ └── viewer.properties │ │ ├── he │ │ │ └── viewer.properties │ │ ├── hi-IN │ │ │ └── viewer.properties │ │ ├── hr │ │ │ └── viewer.properties │ │ ├── hsb │ │ │ └── viewer.properties │ │ ├── hu │ │ │ └── viewer.properties │ │ ├── hy-AM │ │ │ └── viewer.properties │ │ ├── ia │ │ │ └── viewer.properties │ │ ├── id │ │ │ └── viewer.properties │ │ ├── is │ │ │ └── viewer.properties │ │ ├── it │ │ │ └── viewer.properties │ │ ├── ja │ │ │ └── viewer.properties │ │ ├── ka │ │ │ └── viewer.properties │ │ ├── kab │ │ │ └── viewer.properties │ │ ├── kk │ │ │ └── viewer.properties │ │ ├── km │ │ │ └── viewer.properties │ │ ├── kn │ │ │ └── viewer.properties │ │ ├── ko │ │ │ └── viewer.properties │ │ ├── lij │ │ │ └── viewer.properties │ │ ├── lo │ │ │ └── viewer.properties │ │ ├── locale.properties │ │ ├── lt │ │ │ └── viewer.properties │ │ ├── ltg │ │ │ └── viewer.properties │ │ ├── lv │ │ │ └── viewer.properties │ │ ├── mk │ │ │ └── viewer.properties │ │ ├── mr │ │ │ └── viewer.properties │ │ ├── ms │ │ │ └── viewer.properties │ │ ├── my │ │ │ └── viewer.properties │ │ ├── nb-NO │ │ │ └── viewer.properties │ │ ├── ne-NP │ │ │ └── viewer.properties │ │ ├── nl │ │ │ └── viewer.properties │ │ ├── nn-NO │ │ │ └── viewer.properties │ │ ├── oc │ │ │ └── viewer.properties │ │ ├── pa-IN │ │ │ └── viewer.properties │ │ ├── pl │ │ │ └── viewer.properties │ │ ├── pt-BR │ │ │ └── viewer.properties │ │ ├── pt-PT │ │ │ └── viewer.properties │ │ ├── rm │ │ │ └── viewer.properties │ │ ├── ro │ │ │ └── viewer.properties │ │ ├── ru │ │ │ └── viewer.properties │ │ ├── scn │ │ │ └── viewer.properties │ │ ├── si │ │ │ └── viewer.properties │ │ ├── sk │ │ │ └── viewer.properties │ │ ├── sl │ │ │ └── viewer.properties │ │ ├── son │ │ │ └── viewer.properties │ │ ├── sq │ │ │ └── viewer.properties │ │ ├── sr │ │ │ └── viewer.properties │ │ ├── sv-SE │ │ │ └── viewer.properties │ │ ├── ta │ │ │ └── viewer.properties │ │ ├── te │ │ │ └── viewer.properties │ │ ├── th │ │ │ └── viewer.properties │ │ ├── tl │ │ │ └── viewer.properties │ │ ├── tr │ │ │ └── viewer.properties │ │ ├── trs │ │ │ └── viewer.properties │ │ ├── uk │ │ │ └── viewer.properties │ │ ├── ur │ │ │ └── viewer.properties │ │ ├── uz │ │ │ └── viewer.properties │ │ ├── vi │ │ │ └── viewer.properties │ │ ├── wo │ │ │ └── viewer.properties │ │ ├── xh │ │ │ └── viewer.properties │ │ ├── zh-CN │ │ │ └── viewer.properties │ │ └── zh-TW │ │ │ └── viewer.properties │ │ ├── viewer.css │ │ ├── viewer.html │ │ ├── viewer.js │ │ └── viewer.js.map └── test │ ├── commands │ ├── CollectionSharingITest.scala │ └── VerifyIngestionTest.scala │ ├── controllers │ ├── api │ │ ├── EmailsTest.scala │ │ ├── EventsTest.scala │ │ ├── ExtractorITest.scala │ │ ├── SearchTest.scala │ │ ├── UsersTest.scala │ │ ├── WorkspaceSearchITest.scala │ │ ├── WorkspaceSharingITest.scala │ │ └── WorkspacesITest.scala │ └── genesis │ │ └── GenesisTest.scala │ ├── extraction │ └── WorkerTest.scala │ ├── model │ ├── UriTest.scala │ ├── frontend │ │ ├── HighlightableTextTest.scala │ │ └── TreeEntryTest.scala │ └── ingestion │ │ └── FileContextTest.scala │ ├── resources │ ├── META-INF │ │ └── services │ │ │ └── javax.ws.rs.client.ClientBuilder │ ├── ingestme.zip │ ├── ingestme │ │ ├── Georgia_opposition_NATO-Eng-F.doc │ │ ├── directory │ │ │ ├── A hundred years.pdf │ │ │ └── shared.txt │ │ ├── Справочник │ │ │ └── общий.txt │ │ ├── دليل │ │ │ └── اشترك.txt │ │ ├── ディレクトリ │ │ │ └── 共有.txt │ │ ├── 目录 │ │ │ └── 共享.txt │ │ └── 💩 │ │ │ └── 🎁.txt │ └── logback-test.xml │ ├── services │ ├── Neo4JITest.scala │ ├── TestTypeDetector.scala │ ├── index │ │ ├── ElasticsearchEventsITest.scala │ │ ├── ElasticsearchPagesITest.scala │ │ ├── ElasticsearchResourcesITest.scala │ │ ├── ElasticsearchTablesITest.scala │ │ └── SearchContextTest.scala │ ├── manifest │ │ └── Neo4JManifestITest.scala │ ├── observability │ │ └── ExtractorStatusTest.scala │ └── previewing │ │ └── HtmlPreviewGeneratorTest.scala │ ├── test │ ├── AttemptValues.scala │ ├── EmptyAppLoader.scala │ ├── SomePatience.scala │ ├── TestAnnotations.scala │ ├── TestAuthActionBuilder.scala │ ├── TestEventsService.scala │ ├── TestObjectStorage.scala │ ├── TestPostgresClient.scala │ ├── TestPreviewService.scala │ ├── TestUserManagement.scala │ ├── fixtures │ │ └── GoogleAuthenticator.scala │ └── integration │ │ ├── ElasticSearchTestContainer.scala │ │ ├── ElasticsearchTestService.scala │ │ ├── Helpers.scala │ │ ├── Neo4jTestContainer.scala │ │ └── Neo4jTestService.scala │ ├── users │ └── Neo4jUserManagementITest.scala │ └── utils │ ├── IndexTestHelpers.scala │ ├── attempt │ └── AttemptTest.scala │ ├── auth │ ├── AuthActionBuilderTest.scala │ ├── providers │ │ ├── DatabaseUserProviderTest.scala │ │ └── PanDomainUserProviderTest.scala │ └── totp │ │ └── TotpGeneratorTest.scala │ └── controller │ └── DownloadHelperTest.scala ├── build.sbt ├── cli ├── README.md └── src │ ├── main │ ├── resources │ │ └── logback.xml │ └── scala │ │ └── com │ │ └── gu │ │ └── pfi │ │ └── cli │ │ ├── DeleteIngestions.scala │ │ ├── HashFiles.scala │ │ ├── Main.scala │ │ ├── Options.scala │ │ ├── RunIngestion.scala │ │ ├── credentials │ │ ├── CliCredentials.scala │ │ └── CliCredentialsStore.scala │ │ ├── ingestion │ │ ├── CliFileWalker.scala │ │ ├── CliIngestionPipeline.scala │ │ └── IngestionSource.scala │ │ ├── model │ │ ├── ConflictBehaviour.scala │ │ └── VerifyIngestionResult.scala │ │ └── service │ │ ├── CliFiles.scala │ │ ├── CliHttpClient.scala │ │ ├── CliIngestionService.scala │ │ ├── CliServices.scala │ │ ├── CliUsers.scala │ │ ├── CliVeracrypt.scala │ │ └── IngestionS3Client.scala │ └── test │ └── scala │ └── com │ └── gu │ └── pfi │ └── cli │ └── service │ ├── CliIngestionServiceTest.scala │ └── ingestion │ └── CliIngestionPipelineTest.scala ├── common └── src │ └── main │ └── scala │ ├── model │ ├── CliCollection.scala │ ├── CreateCollectionRequest.scala │ ├── CreateIngestion.scala │ ├── Language.scala │ ├── Uri.scala │ ├── VerifyRequest.scala │ ├── VerifyResponse.scala │ ├── index │ │ └── IndexedBlob.scala │ ├── ingestion │ │ ├── IngestMetadata.scala │ │ ├── IngestionFile.scala │ │ ├── OnDiskFileContext.scala │ │ ├── WorkspaceItemContext.scala │ │ └── package.scala │ └── user │ │ └── NewUser.scala │ ├── services │ └── FingerprintServices.scala │ └── utils │ ├── AwsCredentials.scala │ ├── AwsS3Clients.scala │ ├── IngestionVerification.scala │ ├── Logging.scala │ ├── Timing.scala │ ├── VersionedFormat.scala │ └── attempt │ ├── Attempt.scala │ ├── AttemptAwait.scala │ ├── Failure.scala │ └── package.scala ├── docker-compose.yml ├── docs ├── 01-user-quickstart.md ├── 02-admin-quickstart.md ├── 03-developer-tasks.md ├── 04-elasticsearch-mapping.md └── images │ ├── admin_quickstart │ ├── 01_genesis.png │ ├── 02_2fa.png │ ├── 03_create_user.png │ ├── 04_user_register.png │ └── 05_user_management.png │ ├── giant-screenshot.png │ ├── giant_upload_arch.png │ └── user_quickstart │ ├── 01_workspaces_navigation.png │ ├── 02_new_workspace.png │ ├── 03_upload_to_workspace.png │ ├── 04_upload_to_workspace_dialog.png │ ├── 05_workspace_processing.png │ ├── 06_uploaded_file.png │ ├── 07_search_navigation.png │ ├── 08_search.png │ ├── 09_share_workspace.png │ ├── 10_new_folder.png │ ├── 11_select_files.png │ └── 12_file_context_menu.png ├── frontend ├── .gitignore ├── package-lock.json ├── package.json ├── public │ ├── favicon-192.png │ ├── favicon-512.png │ ├── favicon.ico │ ├── index.html │ ├── manifest.json │ └── robots.txt ├── src │ ├── human-date.d.ts │ ├── images │ │ ├── favicon.png │ │ ├── loading.gif │ │ └── masthead.png │ ├── index.js │ ├── js │ │ ├── App.js │ │ ├── actions │ │ │ ├── auth │ │ │ │ ├── getAuthToken.js │ │ │ │ ├── invalidateAuthToken.js │ │ │ │ └── sessionKeepalive.js │ │ │ ├── collections │ │ │ │ ├── getCollection.js │ │ │ │ └── getCollections.js │ │ │ ├── email │ │ │ │ └── getEmailThread.js │ │ │ ├── getFilters.js │ │ │ ├── getNodes.js │ │ │ ├── highlights.ts │ │ │ ├── ingestEvents │ │ │ │ └── updateCurrentWorkspace.ts │ │ │ ├── metrics │ │ │ │ ├── getExtractionFailures.ts │ │ │ │ └── getMimeTypeCoverage.ts │ │ │ ├── pages │ │ │ │ ├── loadPages.ts │ │ │ │ ├── navigateToHighlight.ts │ │ │ │ └── resetPages.ts │ │ │ ├── preferences.js │ │ │ ├── problems.js │ │ │ ├── resources │ │ │ │ ├── clearResource.js │ │ │ │ ├── getComments.ts │ │ │ │ ├── getResource.js │ │ │ │ └── setSelection.ts │ │ │ ├── search │ │ │ │ ├── clearSearch.js │ │ │ │ ├── getSuggestedFields.js │ │ │ │ └── performSearch.js │ │ │ ├── setFilterExpansionState.ts │ │ │ ├── urlParams │ │ │ │ ├── setCurrentHighlight.ts │ │ │ │ ├── setViews.ts │ │ │ │ └── updateSearchQuery.ts │ │ │ ├── users │ │ │ │ ├── addCollectionToUser.js │ │ │ │ ├── createGenesisUser.js │ │ │ │ ├── createUser.ts │ │ │ │ ├── genesisSetupCheck.js │ │ │ │ ├── getMyPermissions.js │ │ │ │ ├── listUsers.ts │ │ │ │ └── setUserPermissions.js │ │ │ └── workspaces │ │ │ │ ├── addFolderToWorkspace.ts │ │ │ │ ├── addResourceToWorkspace.ts │ │ │ │ ├── copyItem.ts │ │ │ │ ├── createWorkspace.ts │ │ │ │ ├── deleteItem.ts │ │ │ │ ├── deleteResourceFromWorkspace.ts │ │ │ │ ├── deleteWorkspace.ts │ │ │ │ ├── getWorkspace.ts │ │ │ │ ├── getWorkspacesMetadata.ts │ │ │ │ ├── moveItem.ts │ │ │ │ ├── renameItem.ts │ │ │ │ ├── renameWorkspace.ts │ │ │ │ ├── reprocessBlob.ts │ │ │ │ ├── setEntryBeingRenamed.ts │ │ │ │ ├── setFocusedEntry.ts │ │ │ │ ├── setNodeAsCollapsed.ts │ │ │ │ ├── setNodeAsExpanded.ts │ │ │ │ ├── setSelectedEntries.ts │ │ │ │ ├── setWorkspaceFollowers.ts │ │ │ │ └── setWorkspaceIsPublic.ts │ │ ├── components │ │ │ ├── Collections │ │ │ │ ├── Collections.js │ │ │ │ ├── CollectionsSidebar │ │ │ │ │ ├── CollectionItem.js │ │ │ │ │ └── CollectionsSidebar.js │ │ │ │ └── CurrentCollection │ │ │ │ │ └── CurrentCollection.tsx │ │ │ ├── Directory │ │ │ │ └── Directory.tsx │ │ │ ├── DownloadLink │ │ │ │ ├── DownloadLink.js │ │ │ │ └── index.js │ │ │ ├── EmailBrowser │ │ │ │ ├── Thread.js │ │ │ │ └── Timeline.js │ │ │ ├── Header.js │ │ │ ├── IngestionEvents │ │ │ │ ├── AllIngestionEvents.tsx │ │ │ │ ├── IngestionEvents.module.css │ │ │ │ ├── IngestionEvents.tsx │ │ │ │ ├── MyUploads.tsx │ │ │ │ ├── types.ts │ │ │ │ ├── updateCurrentCollection.ts │ │ │ │ └── updateCurrentIngestion.ts │ │ │ ├── Login │ │ │ │ ├── Login.js │ │ │ │ └── SessionKeepalive.js │ │ │ ├── PageViewer │ │ │ │ ├── Controls.module.css │ │ │ │ ├── Controls.tsx │ │ │ │ ├── FindInput.module.css │ │ │ │ ├── FindInput.tsx │ │ │ │ ├── Page.module.css │ │ │ │ ├── Page.tsx │ │ │ │ ├── PageCache.ts │ │ │ │ ├── PageHighlight.module.css │ │ │ │ ├── PageHighlight.tsx │ │ │ │ ├── PageOverlayText.tsx │ │ │ │ ├── PageViewer.module.css │ │ │ │ ├── PageViewer.tsx │ │ │ │ ├── PdfHelpers.ts │ │ │ │ ├── VirtualScroll.module.css │ │ │ │ ├── VirtualScroll.tsx │ │ │ │ └── model.ts │ │ │ ├── PageViewerOrFallback.tsx │ │ │ ├── ResourceBreadcrumbs │ │ │ │ ├── ResourceTrail.spec.ts │ │ │ │ ├── ResourceTrail.tsx │ │ │ │ └── index.tsx │ │ │ ├── ResourceHandler │ │ │ │ └── ResourceHandler.tsx │ │ │ ├── Search │ │ │ │ ├── Search.js │ │ │ │ ├── SearchBox.js │ │ │ │ ├── SearchStatus.js │ │ │ │ └── SearchVisualizations.js │ │ │ ├── SearchResults │ │ │ │ ├── CompactResultsTable.js │ │ │ │ ├── SearchResult.js │ │ │ │ ├── SearchResults.js │ │ │ │ └── visualizations │ │ │ │ │ └── TimeHistogram.js │ │ │ ├── SearchSidebar │ │ │ │ ├── SearchFilter.js │ │ │ │ ├── SearchFilterOption.js │ │ │ │ ├── SearchFilterValue.js │ │ │ │ └── SearchSidebar.js │ │ │ ├── Settings │ │ │ │ ├── About.tsx │ │ │ │ ├── DatasetPermissions.js │ │ │ │ ├── ExtractionFailuresComponent.tsx │ │ │ │ ├── FeatureSwitches.js │ │ │ │ ├── FileTypes │ │ │ │ │ ├── FileTypes.tsx │ │ │ │ │ └── MimeTypeProgress.tsx │ │ │ │ ├── ResourcesForExtractionFailureComponent.tsx │ │ │ │ ├── SettingsSidebar.js │ │ │ │ └── Users.tsx │ │ │ ├── Uploads │ │ │ │ ├── FileApiHelpers.ts │ │ │ │ ├── FileList.tsx │ │ │ │ ├── FilePicker.tsx │ │ │ │ ├── UploadFiles.tsx │ │ │ │ ├── UploadTarget.ts │ │ │ │ └── Uploads.tsx │ │ │ ├── UtilComponents │ │ │ │ ├── Checkbox.js │ │ │ │ ├── DetectClickOutside.tsx │ │ │ │ ├── ErrorBar.js │ │ │ │ ├── HighlightedText.js │ │ │ │ ├── HoverSearchLink.js │ │ │ │ ├── InputSupper │ │ │ │ │ ├── Chip.js │ │ │ │ │ ├── InlineInput.js │ │ │ │ │ ├── LabelSupper.js │ │ │ │ │ ├── SuggestionsPanel.js │ │ │ │ │ └── index.js │ │ │ │ ├── KeyboardShortcut.js │ │ │ │ ├── MenuChevron.tsx │ │ │ │ ├── Modal.js │ │ │ │ ├── ModalAction.tsx │ │ │ │ ├── PageNavigator.js │ │ │ │ ├── ProgressAnimation.js │ │ │ │ ├── ProgressBar.js │ │ │ │ ├── SearchLink.js │ │ │ │ ├── SelectionPopover.js │ │ │ │ ├── SidebarSearchLink.js │ │ │ │ ├── Token.tsx │ │ │ │ ├── TreeBrowser │ │ │ │ │ ├── ItemName.tsx │ │ │ │ │ ├── Leaf.tsx │ │ │ │ │ ├── MagicTextInput.tsx │ │ │ │ │ ├── Node.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── Wizard.js │ │ │ │ ├── documentTitle.js │ │ │ │ └── documentTitle.spec.js │ │ │ ├── users │ │ │ │ ├── CreateGenesisUser.js │ │ │ │ ├── CreateNewUser.js │ │ │ │ ├── Register.js │ │ │ │ ├── RegisterUser.js │ │ │ │ └── Setup2Fa.js │ │ │ ├── viewer │ │ │ │ ├── AddToWorkspaceModal.tsx │ │ │ │ ├── CommentHighlighter.tsx │ │ │ │ ├── CommentPanel │ │ │ │ │ ├── AddComment.tsx │ │ │ │ │ ├── CommentPanel.spec.tsx │ │ │ │ │ └── CommentPanel.tsx │ │ │ │ ├── DeleteButtonModal.tsx │ │ │ │ ├── DocumentMetadata.js │ │ │ │ ├── DownloadButton.tsx │ │ │ │ ├── DownloadModal.js │ │ │ │ ├── EmailDetails.js │ │ │ │ ├── EmailMetadata.js │ │ │ │ ├── EmbeddedPdfViewer.tsx │ │ │ │ ├── HighlightToggle.js │ │ │ │ ├── LazyTreeBrowser.tsx │ │ │ │ ├── PageViewer │ │ │ │ │ ├── PagePreview.tsx │ │ │ │ │ ├── PageViewer.tsx │ │ │ │ │ ├── PageViewerStatusBar.tsx │ │ │ │ │ ├── pageViewerApi.ts │ │ │ │ │ └── pageViewerPdf.ts │ │ │ │ ├── PagedBrowser.tsx │ │ │ │ ├── Preview.js │ │ │ │ ├── PreviewSwitcher.js │ │ │ │ ├── StatusBar.js │ │ │ │ ├── TablePreview.tsx │ │ │ │ ├── TextPopover.js │ │ │ │ ├── TextPreview.tsx │ │ │ │ ├── Viewer.tsx │ │ │ │ ├── ViewerActions.js │ │ │ │ └── ViewerSidebar.js │ │ │ └── workspace │ │ │ │ ├── ConfirmModal.tsx │ │ │ │ ├── CopyOrMoveModal.tsx │ │ │ │ ├── CreateFolderModal.tsx │ │ │ │ ├── CreateWorkspaceModal.tsx │ │ │ │ ├── ShareWorkspaceModal.tsx │ │ │ │ ├── WorkspacePublicInfoIcon.tsx │ │ │ │ ├── WorkspacePublicMessage.tsx │ │ │ │ ├── WorkspaceSummary.tsx │ │ │ │ ├── Workspaces.tsx │ │ │ │ ├── WorkspacesSidebar.tsx │ │ │ │ └── WorkspacesSidebarItem.tsx │ │ ├── eui-components │ │ │ ├── GiantEuiApp.tsx │ │ │ ├── GiantEuiHeader.tsx │ │ │ ├── GiantEuiLeftHandNav.tsx │ │ │ ├── GiantEuiSearchResultCount.tsx │ │ │ ├── GiantEuiSearchResultNav.tsx │ │ │ ├── GiantEuiSearchResults.tsx │ │ │ ├── GiantEuiSettings.tsx │ │ │ ├── GiantEuiWorkspace.tsx │ │ │ └── displayConstants.ts │ │ ├── module.d.ts │ │ ├── reducers │ │ │ ├── appReducer.js │ │ │ ├── authReducer.js │ │ │ ├── clusterReducer.js │ │ │ ├── collectionsReducer.js │ │ │ ├── descendantResourcesReducer.js │ │ │ ├── emailsReducer.js │ │ │ ├── expandedFiltersReducer.ts │ │ │ ├── filtersReducer.js │ │ │ ├── highlightsReducer.ts │ │ │ ├── index.js │ │ │ ├── isLoadingResourceReducer.ts │ │ │ ├── metricsReducer.ts │ │ │ ├── pagesReducer.ts │ │ │ ├── resourceReducer.ts │ │ │ ├── searchReducer.js │ │ │ ├── urlParamsReducer.ts │ │ │ ├── usersReducer.js │ │ │ └── workspacesReducer.ts │ │ ├── services │ │ │ ├── AuthApi.js │ │ │ ├── BlobApi.ts │ │ │ ├── ClusterApi.js │ │ │ ├── CollectionsApi.ts │ │ │ ├── CommentsApi.ts │ │ │ ├── DocumentApi.js │ │ │ ├── EmailApi.js │ │ │ ├── FiltersApi.js │ │ │ ├── MetricsApi.ts │ │ │ ├── PreviewApi.js │ │ │ ├── ResourceApi.ts │ │ │ ├── SearchApi.js │ │ │ ├── UserApi.js │ │ │ └── WorkspaceApi.ts │ │ ├── types │ │ │ ├── Auth.ts │ │ │ ├── Cluster.js │ │ │ ├── Collection.ts │ │ │ ├── Config.ts │ │ │ ├── Email.js │ │ │ ├── ExtractionFailures.ts │ │ │ ├── Match.ts │ │ │ ├── MimeType.ts │ │ │ ├── Query.ts │ │ │ ├── Resource.ts │ │ │ ├── SearchFilter.js │ │ │ ├── SearchResults.js │ │ │ ├── SearchResults.ts │ │ │ ├── SuggestedFields.js │ │ │ ├── Token.js │ │ │ ├── Tree.ts │ │ │ ├── User.ts │ │ │ ├── WizardSlide.js │ │ │ ├── Workspaces.ts │ │ │ └── redux │ │ │ │ ├── GiantActions.ts │ │ │ │ ├── GiantDispatch.ts │ │ │ │ └── GiantState.ts │ │ └── util │ │ │ ├── LruCache.ts │ │ │ ├── UrlParameters.js │ │ │ ├── UrlParameters.spec.js │ │ │ ├── auth │ │ │ ├── authFetch.ts │ │ │ ├── authUploadWithProgress.ts │ │ │ └── handleResponseFromAuthRequest.ts │ │ │ ├── buildLink.js │ │ │ ├── commentUtils.ts │ │ │ ├── history.js │ │ │ ├── isLoggedIn.js │ │ │ ├── keyboardShortcuts.js │ │ │ ├── markdownToHtml.js │ │ │ ├── markdownToHtml.spec.js │ │ │ ├── parseDate.js │ │ │ ├── parseDate.spec.js │ │ │ ├── readableFileSize.js │ │ │ ├── readableFileSize.spec.js │ │ │ ├── regexEscape.js │ │ │ ├── resourceUtils.ts │ │ │ ├── store.js │ │ │ ├── storeMiddleware.js │ │ │ ├── stringUtils.spec.ts │ │ │ ├── stringUtils.ts │ │ │ ├── styleLocalization.js │ │ │ ├── stylesheets │ │ │ ├── EUIStyles.tsx │ │ │ ├── OriginalGiantStyles.tsx │ │ │ └── StylesheetLoader.tsx │ │ │ ├── treeUtils.spec.tsx │ │ │ ├── treeUtils.ts │ │ │ ├── workspaceUtils.fixtures.ts │ │ │ ├── workspaceUtils.spec.ts │ │ │ └── workspaceUtils.ts │ ├── react-app-env.d.ts │ ├── setupProxy.js │ └── stylesheets │ │ ├── abstracts │ │ └── _variables.scss │ │ ├── base │ │ ├── _base.scss │ │ ├── _dataTable.scss │ │ ├── _layout.scss │ │ └── _typography.scss │ │ ├── components │ │ ├── _bignum.scss │ │ ├── _buttons.scss │ │ ├── _checkbox.scss │ │ ├── _comments.scss │ │ ├── _coverage.scss │ │ ├── _directory.scss │ │ ├── _document.scss │ │ ├── _email.scss │ │ ├── _error-bar.scss │ │ ├── _file-browser.scss │ │ ├── _forms.scss │ │ ├── _header.scss │ │ ├── _help.scss │ │ ├── _hover-search-link.scss │ │ ├── _input-supper.scss │ │ ├── _lazy-tree-browser.scss │ │ ├── _modal-action.scss │ │ ├── _modal.scss │ │ ├── _node-browser.scss │ │ ├── _page-navigator.scss │ │ ├── _page.scss │ │ ├── _progress-bar.scss │ │ ├── _resource-browser.scss │ │ ├── _search.scss │ │ ├── _select-list.scss │ │ ├── _select.scss │ │ ├── _sidebar.scss │ │ ├── _sparkchart.scss │ │ ├── _textpopover.scss │ │ ├── _timeline.scss │ │ ├── _upload-dialog.scss │ │ ├── _upload-files.scss │ │ ├── _users.scss │ │ ├── _viewer.scss │ │ ├── _wizard.scss │ │ └── _workspace.scss │ │ ├── eui-main.scss │ │ ├── main.scss │ │ └── vendor │ │ ├── _normalize.scss │ │ └── _progressAnimation.scss └── tsconfig.json ├── postgres ├── .tool-versions ├── cdk │ ├── .gitignore │ ├── .nvmrc │ ├── README.md │ ├── bin │ │ └── cdk.ts │ ├── cdk.json │ ├── jest.setup.js │ ├── lib │ │ ├── __snapshots__ │ │ │ └── giant.test.ts.snap │ │ ├── giant.test.ts │ │ └── giant.ts │ ├── package-lock.json │ ├── package.json │ └── tsconfig.json └── migrate-db │ ├── .gitignore │ ├── .nvmrc │ ├── esbuild-runner.config.js │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── index.ts │ └── migrations │ │ ├── 001.do.create_table_ingestion_events.sql │ │ └── 001.undo.create_table_ingestion_events.sql │ └── tsconfig.json ├── project ├── build.properties └── plugins.sbt ├── riff-raff.yaml ├── scripts ├── ci.sh ├── cluster-setup.sh ├── db │ ├── migrate-db-code.sh │ ├── migrate-db-local.sh │ ├── migrate-db-prod.sh │ ├── psql-code.sh │ ├── psql-local.sh │ └── psql-prod.sh ├── setup.sh ├── setup_whisper.sh ├── start-backend.sh ├── start-frontend.sh └── wipe_data.sh └── util ├── local-cerebro.conf └── nginx-mapping.yml /.github/workflows/snyk.yml: -------------------------------------------------------------------------------- 1 | name: Snyk 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | security: 11 | uses: guardian/.github/.github/workflows/sbt-node-snyk.yml@main 12 | with: 13 | ORG: guardian-investigations 14 | secrets: 15 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} 16 | 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.histogram 3 | fingerprint.cache 4 | .idea 5 | neo4j-transaction-*.log 6 | timing-*.csv 7 | node_modules/ 8 | minio-data/ 9 | logs/ 10 | backend/public/build 11 | npm-debug.log 12 | .DS_Store 13 | backend/conf/site.conf 14 | .bsp/ 15 | .bloop/ 16 | .metals/ 17 | metals.sbt -------------------------------------------------------------------------------- /.java-version: -------------------------------------------------------------------------------- 1 | 11.0 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs 18.16.0 2 | java corretto-11.0.16.8.1 3 | -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | brew "tesseract" 2 | cask "libreoffice" 3 | cask "wkhtmltopdf" 4 | brew "ocrmypdf" 5 | brew "imagemagick" 6 | brew "qpdf" 7 | brew "ffmpeg" 8 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @guardian/investigations 2 | -------------------------------------------------------------------------------- /backend/app/commands/Command.scala: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import utils.attempt.{Attempt, Failure} 4 | 5 | import scala.concurrent.Future 6 | 7 | trait Command[T] { 8 | def process(): T 9 | } 10 | 11 | trait CommandCanFail[T] extends Command[Either[Failure, T]] 12 | trait AttemptCommand[T] extends Command[Attempt[T]] 13 | 14 | // Consider using AttemptCommand instead 15 | trait CommandCanFailAsync[T] extends Command[Future[Either[Failure, T]]] 16 | -------------------------------------------------------------------------------- /backend/app/commands/CreateCollection.scala: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import model.Uri 4 | import model.manifest.Collection 5 | import services.manifest.Manifest 6 | import utils.UriCleaner 7 | import utils.attempt.Attempt 8 | 9 | import scala.concurrent.ExecutionContext 10 | 11 | case class CreateCollection(name: String, username: String, manifest: Manifest) 12 | (implicit ec: ExecutionContext) extends AttemptCommand[Collection] { 13 | def process(): Attempt[Collection] = { 14 | val id = UriCleaner.clean(name) 15 | 16 | manifest.getCollection(Uri(id)).transform( 17 | collection => 18 | Attempt.Right(collection), 19 | _ => 20 | manifest.insertCollection(id, name, username) 21 | ) 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /backend/app/commands/GetBlobObjectData.scala: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import model.{ObjectData, ObjectMetadata, Uri} 4 | import services.ObjectStorage 5 | import services.manifest.Manifest 6 | import utils.attempt.Failure 7 | 8 | case class GetBlobObjectData(uri: Uri, manifest: Manifest, blobStorage: ObjectStorage) extends CommandCanFail[ObjectData] { 9 | override def process(): Either[Failure, ObjectData] = for { 10 | blob <- manifest.getBlob(uri) 11 | data <- blobStorage.get(uri.toStoragePath) 12 | } yield ObjectData(data, ObjectMetadata(blob.size, blob.mimeType.head.mimeType)) 13 | } 14 | -------------------------------------------------------------------------------- /backend/app/commands/GetEmailThread.scala: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import services.manifest.Manifest 4 | import utils.attempt.Failure 5 | 6 | case class EmailThread(uri: String, manifest: Manifest) extends CommandCanFail[EmailThread] { 7 | def process(): Either[Failure, EmailThread] = ??? 8 | } -------------------------------------------------------------------------------- /backend/app/controllers/api/Filters.scala: -------------------------------------------------------------------------------- 1 | package controllers.api 2 | 3 | import commands._ 4 | import play.api.libs.json._ 5 | import services.annotations.Annotations 6 | import services.manifest.Manifest 7 | import utils.controller.{AuthApiController, AuthControllerComponents} 8 | 9 | class Filters(val controllerComponents: AuthControllerComponents, manifest: Manifest, annotations: Annotations) 10 | extends AuthApiController { 11 | 12 | def getFilters = ApiAction.attempt { req => 13 | GetFilters(manifest, controllerComponents.users, annotations, req.user.username).process().map(docs => Ok(Json.toJson(docs))) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /backend/app/controllers/api/Ingestion.scala: -------------------------------------------------------------------------------- 1 | package controllers.api 2 | 3 | import model.user.UserPermission.CanPerformAdminOperations 4 | import play.api.mvc._ 5 | import services.S3IngestStorage 6 | import utils.attempt._ 7 | import utils.controller.{AuthApiController, AuthControllerComponents} 8 | 9 | class Ingestion(override val controllerComponents: AuthControllerComponents, storage: S3IngestStorage) 10 | extends AuthApiController { 11 | 12 | def retryDeadLetterFiles(): Action[AnyContent] = ApiAction.attempt { req => 13 | checkPermission(CanPerformAdminOperations, req) { 14 | Attempt.fromEither(storage.retryDeadLetters() match { 15 | case Right(_) => Right(Ok("Sent all dead letter files for reingest")) 16 | case Left(failure) => Left(failure) 17 | }) 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /backend/app/controllers/api/MimeTypes.scala: -------------------------------------------------------------------------------- 1 | package controllers.api 2 | 3 | import model.user.UserPermission.CanPerformAdminOperations 4 | import play.api.libs.json.Json 5 | import services.manifest.Manifest 6 | import utils.MimeDetails 7 | import utils.attempt.Attempt 8 | import utils.controller.{AuthApiController, AuthControllerComponents} 9 | 10 | class MimeTypes(val controllerComponents: AuthControllerComponents, manifest: Manifest) extends AuthApiController { 11 | def getDetails = ApiAction { 12 | Right(Ok(Json.toJson(MimeDetails.displayMap))) 13 | } 14 | 15 | def getCoverage = ApiAction.attempt { req => 16 | checkPermission(CanPerformAdminOperations, req) { 17 | Attempt.fromEither(manifest.getMimeTypesCoverage).map(coverage => Ok(Json.toJson(coverage))) 18 | } 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /backend/app/extraction/Extractor.scala: -------------------------------------------------------------------------------- 1 | package extraction 2 | 3 | import java.io.InputStream 4 | 5 | import model.ingestion.WorkspaceItemContext 6 | import model.{Language, Uri} 7 | import model.manifest.{Blob, MimeType} 8 | import utils.attempt.Failure 9 | 10 | case class ExtractionParams(ingestion: String, languages: List[Language], parentBlobs: List[Uri], workspace: Option[WorkspaceItemContext]) 11 | 12 | trait Extractor { 13 | def canProcessMimeType: String => Boolean 14 | 15 | def name: String = this.getClass.getSimpleName 16 | 17 | def indexing: Boolean 18 | 19 | def priority: Int 20 | 21 | def cost(mimeType: MimeType, size: Long): Long = size 22 | 23 | def extract(blob: Blob, inputStream: InputStream, params: ExtractionParams): Either[Failure, Unit] 24 | 25 | def external: Boolean = false 26 | } -------------------------------------------------------------------------------- /backend/app/extraction/MimeTypeMapper.scala: -------------------------------------------------------------------------------- 1 | package extraction 2 | 3 | // Used during insertion of blobs to associate a blob with its appropriate extractor 4 | class MimeTypeMapper { 5 | var extractors: List[Extractor] = Nil 6 | 7 | def getExtractorsFor(mimeType: String): List[Extractor] = extractors.filter(_.canProcessMimeType(mimeType)) 8 | def addExtractor(extractor: Extractor): Unit = extractors = extractor :: extractors 9 | } -------------------------------------------------------------------------------- /backend/app/extraction/email/EmailContentTypeCleaner.java: -------------------------------------------------------------------------------- 1 | package extraction.email; 2 | 3 | import jakarta.mail.internet.MimePart; 4 | 5 | // See `mail.mime.contenttypehandler` here: https://docs.oracle.com/javaee/6/api/javax/mail/internet/package-summary.html 6 | // This allows us to intercept crappy MIME types and correct them before the email parser barfs everywhere 7 | public class EmailContentTypeCleaner { 8 | public static String cleanContentType(MimePart mp, String contentType) { 9 | return contentType 10 | .replace("charset='US-ASCII'", "charset=US-ASCII") 11 | .replace("ISO-8859-6-i", "ISO-8859-6"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/app/extraction/email/olm/OlmEmailDetector.scala: -------------------------------------------------------------------------------- 1 | package extraction.email.olm 2 | 3 | import java.io.InputStream 4 | import java.util.zip.ZipInputStream 5 | 6 | import extraction.email.CustomTikaDetector 7 | import org.apache.tika.mime.MediaType 8 | 9 | object OlmEmailDetector extends CustomTikaDetector { 10 | // TODO MRB: is there an actual registered OLM mime type? this is invented 11 | val OLM_MIME_TYPE = "application/vnd.ms-outlook-olm" 12 | 13 | override def detectType(input: InputStream): Option[MediaType] = { 14 | val zipInput = new ZipInputStream(input) 15 | Option(zipInput.getNextEntry) match { 16 | case Some(entry) if entry.getName == "Categories.xml" => 17 | Some(MediaType.parse(OLM_MIME_TYPE)) 18 | 19 | case _ => 20 | None 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/app/model/ObjectData.scala: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import java.io.InputStream 4 | 5 | case class ObjectMetadata(size: Long, mimeType: String) 6 | case class ObjectData(data: InputStream, metadata: ObjectMetadata) 7 | 8 | -------------------------------------------------------------------------------- /backend/app/model/frontend/ClientConfig.scala: -------------------------------------------------------------------------------- 1 | package model.frontend 2 | 3 | import play.api.libs.json._ 4 | import utils.buildinfo.BuildInfo 5 | 6 | // Config which we need to ship to the client to improve UX 7 | // e.g. Knowing the min password length without actually submitting a user creation, or avoiding the 2FA setup page if it's not required. 8 | case class ClientConfig(label: Option[String], 9 | readOnly: Boolean, 10 | userProvider: String, 11 | authConfig: Map[String, JsValue], 12 | hideDownloadButton: Boolean, 13 | buildInfo: Map[String, String] = BuildInfo.toMap.view.mapValues(_.toString).toMap) 14 | 15 | object ClientConfig { 16 | implicit val format = Json.format[ClientConfig] 17 | } 18 | -------------------------------------------------------------------------------- /backend/app/model/frontend/EmailThread.scala: -------------------------------------------------------------------------------- 1 | package model.frontend 2 | 3 | import model.{Email, Recipient} 4 | 5 | case class EmailThread(emails: List[List[ThreadEmail]]) 6 | 7 | // TODO attachments 8 | case class ThreadEmail(uri: String, subject: String, inReplyTo: String, recipients: List[Recipient], hasHiddenOutgoing: Boolean) 9 | -------------------------------------------------------------------------------- /backend/app/model/frontend/Filter.scala: -------------------------------------------------------------------------------- 1 | package model.frontend 2 | 3 | import play.api.libs.json._ 4 | 5 | object FilterNames { 6 | case class FilterName(display: String, key: String) 7 | 8 | val workspaces = FilterName("Workspaces", "workspace") 9 | val collections = FilterName("Datasets", "ingestion") 10 | val mimeTypes = FilterName("File Types", "mimeType") 11 | } 12 | case class FilterOption(value: String, display: String, explanation: Option[String] = None, suboptions: Option[List[FilterOption]] = None) 13 | 14 | object FilterOption { 15 | implicit val filterOptionFormat = Json.format[FilterOption] 16 | } 17 | 18 | case class Filter(key: String, display: String, hideable: Boolean, options: List[FilterOption]) 19 | 20 | object Filter { 21 | implicit val filterFormat = Json.format[Filter] 22 | } 23 | -------------------------------------------------------------------------------- /backend/app/model/frontend/Highlight.scala: -------------------------------------------------------------------------------- 1 | package model.frontend 2 | 3 | import play.api.libs.json.Json 4 | 5 | case class Highlight(field: String, display: String, highlight: String) 6 | 7 | object Highlight { 8 | implicit val highlightFormat = Json.format[Highlight] 9 | } 10 | -------------------------------------------------------------------------------- /backend/app/model/frontend/Node.scala: -------------------------------------------------------------------------------- 1 | package model.frontend 2 | 3 | import play.api.libs.json.Json 4 | 5 | case class Node(hostname: String, reachable: Boolean) 6 | 7 | object Node { 8 | implicit val nodeFormat = Json.format[Node] 9 | } 10 | -------------------------------------------------------------------------------- /backend/app/model/frontend/Paging.scala: -------------------------------------------------------------------------------- 1 | package model.frontend 2 | 3 | // Try to ensure we maintain consistency for end-points that support paging 4 | trait Paging[T] { 5 | val hits: Long 6 | val page: Long 7 | val pageSize: Long 8 | val results: List[T] 9 | } 10 | -------------------------------------------------------------------------------- /backend/app/model/frontend/TotpActivation.scala: -------------------------------------------------------------------------------- 1 | package model.frontend 2 | 3 | import play.api.libs.json.Json 4 | 5 | case class TotpActivation(secret: String, code: String) 6 | object TotpActivation { 7 | implicit val formats = Json.format[TotpActivation] 8 | } 9 | -------------------------------------------------------------------------------- /backend/app/model/frontend/user/NewGenesisUser.scala: -------------------------------------------------------------------------------- 1 | package model.frontend.user 2 | 3 | import model.frontend.TotpActivation 4 | import play.api.libs.json.Json 5 | 6 | case class NewGenesisUser(username: String, displayName: String, password: String, totpActivation: Option[TotpActivation]) 7 | 8 | object NewGenesisUser { 9 | implicit val genesisUserFormat = Json.format[NewGenesisUser] 10 | } 11 | -------------------------------------------------------------------------------- /backend/app/model/frontend/user/PartialUser.scala: -------------------------------------------------------------------------------- 1 | package model.frontend.user 2 | 3 | import play.api.libs.json.Json 4 | 5 | /* User model for sending users to and from the SPA */ 6 | case class PartialUser(username: String, displayName: String) 7 | 8 | object PartialUser { 9 | implicit val userFormat = Json.format[PartialUser] 10 | } 11 | -------------------------------------------------------------------------------- /backend/app/model/frontend/user/README.md: -------------------------------------------------------------------------------- 1 | # Frontend User Models 2 | 3 | There are a few models used here for various representations of users 4 | 5 | | Class | Use | 6 | |------------------|--------------------------------------------------| 7 | | NewGenesisUser | When creating a new genesis user | 8 | | NewUser | When creating the skeleton for a new user | 9 | | PartialUser | Simply rendering basic user info to the frontend | 10 | | UserRegistration | When a new user fills out their skeleton | 11 | -------------------------------------------------------------------------------- /backend/app/model/frontend/user/UserRegistration.scala: -------------------------------------------------------------------------------- 1 | package model.frontend.user 2 | 3 | import model.frontend.TotpActivation 4 | import play.api.libs.json.Json 5 | 6 | case class UserRegistration(username: String, previousPassword: String, displayName: String, newPassword: String, totpActivation: Option[TotpActivation]) 7 | 8 | object UserRegistration { 9 | implicit val formats = Json.format[UserRegistration] 10 | } 11 | -------------------------------------------------------------------------------- /backend/app/model/index/Flags.scala: -------------------------------------------------------------------------------- 1 | package model.index 2 | 3 | object Flags { 4 | val unseen = "unseen" 5 | val seen = "seen" 6 | val up = "up" 7 | val down = "down" 8 | 9 | val all = List(unseen, seen, up, down) 10 | 11 | val toDisplay: PartialFunction[String, String] = { 12 | case `unseen` => "Unseen" 13 | case `seen` => "Seen" 14 | case `up` => "Important" 15 | case `down` => "Unimportant" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/app/model/index/IndexedResource.scala: -------------------------------------------------------------------------------- 1 | package model.index 2 | 3 | trait IndexedResource 4 | -------------------------------------------------------------------------------- /backend/app/model/index/TableRow.scala: -------------------------------------------------------------------------------- 1 | package model.index 2 | 3 | import play.api.libs.json.Json 4 | 5 | case class TableRow(sheetName: Option[String], rowIndex: Int, cells: Map[String, String]) 6 | object TableRow { 7 | implicit val tableRowFormat = Json.format[TableRow] 8 | } 9 | -------------------------------------------------------------------------------- /backend/app/model/ingestion/OcrMyPdfFlag.scala: -------------------------------------------------------------------------------- 1 | package model.ingestion 2 | 3 | // See https://ocrmypdf.readthedocs.io/en/latest/advanced.html#when-ocr-is-skipped for details of these flags 4 | // also https://github.com/guardian/giant/pull/68 for a discussion of their use in giant 5 | sealed trait OcrMyPdfFlag { 6 | def flag: String 7 | } 8 | case object RedoOcr extends OcrMyPdfFlag { 9 | val flag = "--redo-ocr" 10 | } 11 | case object SkipText extends OcrMyPdfFlag { 12 | val flag = "--skip-text" 13 | } 14 | case object ForceOcr extends OcrMyPdfFlag { 15 | val flag = "--force-ocr" 16 | } -------------------------------------------------------------------------------- /backend/app/model/manifest/Blob.scala: -------------------------------------------------------------------------------- 1 | package model.manifest 2 | 3 | import model.Uri 4 | import org.neo4j.driver.v1.Value 5 | import play.api.libs.json._ 6 | 7 | case class Blob(uri: Uri, size: Long, mimeType: Set[MimeType]) 8 | 9 | object Blob { 10 | implicit val blobFormat = Json.format[Blob] 11 | 12 | def fromNeo4jValue(blob: Value, mimeTypes: Seq[Value]): Blob = { 13 | Blob(Uri(blob.get("uri").asString()), 14 | blob.get("size").asLong(), mimeTypes.map(MimeType.fromNeo4jValue).toSet) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /backend/app/model/manifest/MimeType.scala: -------------------------------------------------------------------------------- 1 | package model.manifest 2 | 3 | import org.neo4j.driver.v1.Value 4 | import play.api.libs.json.Json 5 | 6 | case class MimeType(mimeType: String) 7 | 8 | object MimeType { 9 | implicit val mimeTypeFormat = Json.format[MimeType] 10 | 11 | def fromNeo4jValue(mimeType: Value): MimeType = { 12 | MimeType( 13 | mimeType = mimeType.get("mimeType").asString() 14 | ) 15 | } 16 | } 17 | 18 | case class MimeTypeCoverage(mimeType: MimeType, humanReadableMimeType: Option[String], 19 | total: Long, todo: Long, done: Long, failed: Long) 20 | 21 | object MimeTypeCoverage { 22 | implicit val mimeCoverageTypeFormat = Json.format[MimeTypeCoverage] 23 | } 24 | -------------------------------------------------------------------------------- /backend/app/model/manifest/UserWithCollections.scala: -------------------------------------------------------------------------------- 1 | package model.manifest 2 | 3 | import model.user.UserPermissions 4 | import play.api.libs.json.{Json, OFormat} 5 | 6 | case class UserWithCollections(username: String, displayName: String, collections: List[String], permissions: UserPermissions) 7 | object UserWithCollections { 8 | implicit val userFormat: OFormat[UserWithCollections] = Json.format[UserWithCollections] 9 | } 10 | -------------------------------------------------------------------------------- /backend/app/model/manifest/WorkItem.scala: -------------------------------------------------------------------------------- 1 | package model.manifest 2 | 3 | import model._ 4 | import model.ingestion.WorkspaceItemContext 5 | 6 | case class WorkItem(blob: Blob, parentBlobs: List[Uri], extractorName: String, ingestion: String, languages: List[Language], workspace: Option[WorkspaceItemContext]) 7 | -------------------------------------------------------------------------------- /backend/app/model/user/BCryptPassword.scala: -------------------------------------------------------------------------------- 1 | package model.user 2 | 3 | import play.api.libs.json._ 4 | 5 | case class BCryptPassword(hash: String) extends AnyVal 6 | 7 | object BCryptPassword { 8 | implicit val BCryptPasswordFormat = new Format[BCryptPassword] { 9 | def writes(password: BCryptPassword): JsValue = JsString(password.hash) 10 | def reads(json: JsValue): JsResult[BCryptPassword] = Reads.StringReads.reads(json).map(BCryptPassword.apply) 11 | } 12 | } -------------------------------------------------------------------------------- /backend/app/model/user/UserPermissions.scala: -------------------------------------------------------------------------------- 1 | package model.user 2 | 3 | import enumeratum._ 4 | import model.user.UserPermission.CanPerformAdminOperations 5 | import play.api.libs.json.{Format, Json} 6 | 7 | sealed trait UserPermission extends EnumEntry 8 | 9 | object UserPermission extends PlayEnum[UserPermission] { 10 | val values = findValues 11 | 12 | case object CanPerformAdminOperations extends UserPermission 13 | } 14 | 15 | case class UserPermissions(granted: Set[UserPermission]) { 16 | def hasPermission(p: UserPermission): Boolean = granted.contains(p) 17 | } 18 | 19 | object UserPermissions { 20 | val default = UserPermissions(Set.empty) 21 | val bigBoss = UserPermissions(Set(CanPerformAdminOperations)) 22 | 23 | implicit val format: Format[UserPermissions] = Json.format[UserPermissions] 24 | } 25 | -------------------------------------------------------------------------------- /backend/app/services/index/IngestionData.scala: -------------------------------------------------------------------------------- 1 | package services.index 2 | 3 | import model.Uri 4 | import model.ingestion.WorkspaceItemContext 5 | import model.manifest.MimeType 6 | import play.api.libs.json.Json 7 | 8 | case class IngestionData(createdAt: Option[Long], lastModifiedAt: Option[Long], mimeTypes: Set[MimeType], uris: Set[Uri], 9 | parentBlobs: List[Uri], ingestion: String, workspace: Option[WorkspaceItemContext]) 10 | 11 | object IngestionData { 12 | implicit val format = Json.format[IngestionData] 13 | } 14 | -------------------------------------------------------------------------------- /backend/app/services/index/Mappings.scala: -------------------------------------------------------------------------------- 1 | package services.index 2 | 3 | object Mappings { 4 | // Do mappings 5 | } 6 | -------------------------------------------------------------------------------- /backend/app/services/index/Pages.scala: -------------------------------------------------------------------------------- 1 | package services.index 2 | 3 | import model.index.{Page, PageResult} 4 | import model.{Language, Uri} 5 | import utils.attempt.Attempt 6 | 7 | trait Pages { 8 | def setup(): Attempt[Pages] 9 | 10 | def addPageContents(uri: Uri, pages: Seq[Page]): Attempt[Unit] 11 | 12 | def getTextPages(uri: Uri, top: Double, bottom: Double, highlightQuery: Option[String]): Attempt[PageResult] 13 | 14 | def getPage(uri: Uri, pageNumber: Int, highlightQuery: Option[String]): Attempt[Page] 15 | } 16 | -------------------------------------------------------------------------------- /backend/app/utils/AllowFrameFilter.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import org.apache.pekko.stream.Materializer 4 | import play.api.mvc.{Filter, RequestHeader, Result} 5 | 6 | import scala.concurrent.{ExecutionContext, Future} 7 | 8 | class AllowFrameFilter(implicit val mat: Materializer, ec: ExecutionContext) extends Filter with Logging { 9 | override def apply(f: RequestHeader => Future[Result])(rh: RequestHeader): Future[Result] = { 10 | val allow = rh.path.startsWith("/third-party/pdfjs") 11 | 12 | if(allow) { 13 | f(rh).map { r => 14 | r.withHeaders("X-Frame-Options" -> "SAMEORIGIN") 15 | } 16 | } else { 17 | f(rh) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/app/utils/BasicStdErrLogger.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import scala.collection.mutable 4 | 5 | class BasicStdErrLogger extends Logging { 6 | val acc = mutable.Buffer[String]() 7 | 8 | def append(line: String): Unit = { 9 | acc.append(line) 10 | 11 | logger.info(line) 12 | } 13 | 14 | def getOutput: String = { 15 | acc.mkString("\n") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/app/utils/EitherTHelper.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import cats.data.EitherT 4 | 5 | import scala.concurrent.{ExecutionContext, Future} 6 | 7 | object EitherTHelper { 8 | implicit class RichEitherTFuture[A, B](eitherT: EitherT[Future, A, B]) { 9 | def recoverF(pf: PartialFunction[Throwable, Either[A, B]])(implicit executor: ExecutionContext): EitherT[Future, A, B] = { 10 | EitherT(eitherT.value.recover(pf)) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/app/utils/Epoch.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import java.time.{OffsetDateTime, ZoneOffset} 4 | 5 | case class Epoch(seconds: Long) extends AnyVal { 6 | def millis = seconds * 1000 7 | } 8 | 9 | object Epoch { 10 | def now = Epoch(System.currentTimeMillis() / 1000L) 11 | def from(dt: OffsetDateTime): Epoch = Epoch(dt.toEpochSecond) 12 | def fromUtc(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) = 13 | Epoch.from(OffsetDateTime.of(2019, 2, 14, 10, 0, 0, 0, ZoneOffset.UTC)) 14 | } 15 | -------------------------------------------------------------------------------- /backend/app/utils/Stopwatch.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | object Stopwatch { 4 | def measure[T](f: => T): (T, Long) = { 5 | val start = System.currentTimeMillis 6 | val result = f 7 | val timeTaken = System.currentTimeMillis - start 8 | (result, timeTaken) 9 | } 10 | 11 | def measureSideEffect(f: => Any): Long = { 12 | measure(f)._2 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/app/utils/Time.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import java.time.{Instant, OffsetDateTime, ZoneId} 4 | 5 | object Time { 6 | implicit class EpochLong(millis: Long) { 7 | def millisToDateTime(zoneId: ZoneId = ZoneId.systemDefault()): OffsetDateTime = 8 | OffsetDateTime.ofInstant(Instant.ofEpochMilli(millis), zoneId) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /backend/app/utils/UriCleaner.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | object UriCleaner { 4 | /** 5 | * Converts a display name into a 'clean' name which can be safely stored as a URI 6 | */ 7 | def clean(text: String): String = text.replace("/", "-") 8 | } 9 | -------------------------------------------------------------------------------- /backend/app/utils/auth/PasswordValidator.scala: -------------------------------------------------------------------------------- 1 | package utils.auth 2 | 3 | import utils.attempt.{Attempt, ClientFailure} 4 | 5 | class PasswordValidator(minPasswordLength: Int) { 6 | def validate(password: String): Attempt[String] = { 7 | if (password.length >= minPasswordLength) { 8 | Attempt.Right(password) 9 | } else { 10 | Attempt.Left(ClientFailure(s"Provided password too short, must be at least $minPasswordLength characters")) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/app/utils/auth/Token.scala: -------------------------------------------------------------------------------- 1 | package utils.auth 2 | 3 | import play.api.libs.json.Json 4 | 5 | case class Token(user: User, issuedAt: Long, refreshedAt: Long, exp: Long, loginExpiry: Long, verificationExpiry: Long) 6 | 7 | object Token { 8 | val USER_KEY = "user" 9 | val ISSUED_AT_KEY = "issuedAt" 10 | val REFRESHED_AT_KEY = "refreshedAt" 11 | val LOGIN_EXPIRY_KEY = "loginExpiry" 12 | val VERIFICATION_EXPIRY_KEY = "verificationExpiry" 13 | val PERMISSIONS = "permissions" 14 | implicit val tokenReads = Json.reads[Token] 15 | } -------------------------------------------------------------------------------- /backend/app/utils/auth/User.scala: -------------------------------------------------------------------------------- 1 | package utils.auth 2 | 3 | import play.api.libs.json.{JsObject, JsString, Json} 4 | import net.logstash.logback.marker.LogstashMarker 5 | import net.logstash.logback.marker.Markers.appendRaw 6 | 7 | 8 | /* User model that is passed into Actions */ 9 | case class User(username: String, displayName: String) { 10 | def asLogMarker: LogstashMarker = appendRaw("user", 11 | // { user: { name: xyz } } for compatibility with the default filebeat index template 12 | Json.stringify(JsObject(Seq("name" -> JsString(username))))) 13 | } 14 | 15 | object User { 16 | implicit val userFormat = Json.format[User] 17 | } 18 | -------------------------------------------------------------------------------- /backend/app/utils/auth/package.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import play.api.mvc.Security.AuthenticatedRequest 4 | 5 | package object auth { 6 | type UserIdentityRequest[A] = AuthenticatedRequest[A, User] 7 | } 8 | -------------------------------------------------------------------------------- /backend/app/utils/auth/totp/Algorithm.scala: -------------------------------------------------------------------------------- 1 | package utils.auth.totp 2 | 3 | case class Algorithm(alg: String, secretLength: Int) 4 | object Algorithm { 5 | val HmacSHA1 = Algorithm("HmacSHA1", 20) 6 | val HmacSHA256 = Algorithm("HmacSHA256", 32) 7 | val HmacSHA512 = Algorithm("HmacSHA512", 64) 8 | } 9 | -------------------------------------------------------------------------------- /backend/app/utils/auth/totp/Secret.scala: -------------------------------------------------------------------------------- 1 | package utils.auth.totp 2 | 3 | case class Secret(data: Vector[Byte]) { 4 | def toBase32: String = Totp.bytesToBase32(data) 5 | } 6 | object HexSecret { 7 | def apply(hexSecret:String): Secret = Secret(Totp.hexStrToBytes(hexSecret)) 8 | } 9 | object Base32Secret { 10 | def apply(base32Secret:String): Secret = Secret(Totp.base32ToBytes(base32Secret)) 11 | } -------------------------------------------------------------------------------- /backend/app/utils/auth/totp/TfaToken.scala: -------------------------------------------------------------------------------- 1 | package utils.auth.totp 2 | 3 | case class TfaToken(secret: String, url: String) 4 | -------------------------------------------------------------------------------- /backend/app/utils/aws/AwsErrors.scala: -------------------------------------------------------------------------------- 1 | package utils.aws 2 | 3 | import com.amazonaws.services.s3.model.AmazonS3Exception 4 | import com.amazonaws.{AmazonServiceException, SdkClientException} 5 | import utils.attempt.{AwsSdkFailure, Failure, NotFoundFailure} 6 | 7 | object AwsErrors { 8 | val exceptionToFailure: PartialFunction[Throwable, Failure] = { 9 | case err: AmazonS3Exception if err.getStatusCode == 404 => 10 | NotFoundFailure(err.toString) 11 | case ase: AmazonServiceException => 12 | AwsSdkFailure(ase) // TODO/SAH: Use a more specific failure that captures the error codes etc 13 | case sce: SdkClientException => 14 | AwsSdkFailure(sce) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /backend/app/utils/aws/CreateBuckets.scala: -------------------------------------------------------------------------------- 1 | package utils.aws 2 | 3 | import com.typesafe.config.ConfigFactory 4 | import services.Config 5 | 6 | object CreateBuckets { 7 | def createIfNotExists(): List[String] = { 8 | val config = Config(ConfigFactory.load()) 9 | val s3Client = new S3Client(config.s3)(scala.concurrent.ExecutionContext.Implicits.global) 10 | 11 | config.s3.buckets.all.foldLeft(List.empty[String]) { (acc, bucket) => 12 | if(!s3Client.doesBucketExist(bucket)) { 13 | s3Client.aws.createBucket(bucket) 14 | acc :+ bucket 15 | } else { 16 | acc 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/app/utils/controller/NoAuthApiController.scala: -------------------------------------------------------------------------------- 1 | package utils.controller 2 | 3 | import play.api.mvc.{ActionBuilder, AnyContent, Request} 4 | import utils.attempt.Failure 5 | 6 | trait NoAuthApiController extends PfiApiController[Request] { 7 | final override def actionBuilder: ActionBuilder[Request, AnyContent] = controllerComponents.actionBuilder 8 | final override def failureToResult(r: Request[_], error: Failure) = 9 | FailureToResultMapper.failureToResult(error) 10 | } 11 | -------------------------------------------------------------------------------- /backend/lib/mbox-2.0.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/lib/mbox-2.0.2.jar -------------------------------------------------------------------------------- /backend/lib/msgparser-1.2.0-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/lib/msgparser-1.2.0-SNAPSHOT.jar -------------------------------------------------------------------------------- /backend/public/third-party/pdf.worker.min.js: -------------------------------------------------------------------------------- 1 | ../../../frontend/node_modules/pdfjs-dist/build/pdf.worker.min.mjs -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78-EUC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78-EUC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78-RKSJ-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78-RKSJ-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78ms-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78ms-RKSJ-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78ms-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/78ms-RKSJ-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/83pv-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/83pv-RKSJ-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/90ms-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/90ms-RKSJ-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/90ms-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/90ms-RKSJ-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/90msp-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/90msp-RKSJ-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/90msp-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/90msp-RKSJ-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/90pv-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/90pv-RKSJ-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/90pv-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/90pv-RKSJ-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Add-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Add-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Add-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Add-RKSJ-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Add-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Add-RKSJ-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Add-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Add-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-0.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-0.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-1.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-1.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-2.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-3.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-3.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-4.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-4.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-5.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-5.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-6.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-6.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-UCS2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-CNS1-UCS2.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-0.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-0.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-1.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-1.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-2.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-3.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-3.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-4.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-4.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-5.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-5.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-UCS2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-GB1-UCS2.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-0.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-0.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-1.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-1.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-2.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-3.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-3.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-4.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-4.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-5.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-5.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-6.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-6.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-UCS2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Japan1-UCS2.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Korea1-0.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Korea1-0.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Korea1-1.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Korea1-1.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Korea1-2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Korea1-2.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Korea1-UCS2.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Adobe-Korea1-UCS2.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/B5-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/B5-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/B5pc-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/B5pc-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/B5pc-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/B5pc-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/CNS-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/CNS-EUC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/CNS-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/CNS-EUC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/CNS1-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/CNS1-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/CNS1-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/CNS1-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/CNS2-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/CNS2-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/CNS2-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/CNS2-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/ETHK-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/ETHK-B5-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/ETHK-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/ETHK-B5-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/ETen-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/ETen-B5-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/ETen-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/ETen-B5-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/ETenms-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/ETenms-B5-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/ETenms-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/ETenms-B5-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/EUC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/EUC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Ext-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Ext-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Ext-RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Ext-RKSJ-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Ext-RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Ext-RKSJ-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Ext-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Ext-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GB-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GB-EUC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GB-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GB-EUC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GB-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GB-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GB-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GB-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBK-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBK-EUC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBK-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBK-EUC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBK2K-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBK2K-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBK2K-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBK2K-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBKp-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBKp-EUC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBKp-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBKp-EUC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBT-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBT-EUC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBT-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBT-EUC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBT-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBT-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBT-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBT-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBTpc-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBTpc-EUC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBTpc-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBTpc-EUC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBpc-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBpc-EUC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBpc-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/GBpc-EUC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKdla-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKdla-B5-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKdla-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKdla-B5-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKdlb-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKdlb-B5-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKdlb-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKdlb-B5-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKgccs-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKgccs-B5-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKgccs-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKgccs-B5-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKm314-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKm314-B5-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKm314-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKm314-B5-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKm471-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKm471-B5-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKm471-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKm471-B5-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKscs-B5-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKscs-B5-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKscs-B5-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/HKscs-B5-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Hankaku.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Hankaku.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Hiragana.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Hiragana.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSC-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSC-EUC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSC-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSC-EUC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSC-Johab-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSC-Johab-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSC-Johab-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSC-Johab-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSCms-UHC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSCms-UHC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSCms-UHC-HW-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSCms-UHC-HW-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSCms-UHC-HW-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSCms-UHC-HW-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSCms-UHC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSCms-UHC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSCpc-EUC-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSCpc-EUC-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSCpc-EUC-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/KSCpc-EUC-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Katakana.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Katakana.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/NWP-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/NWP-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/NWP-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/NWP-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/RKSJ-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/RKSJ-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/RKSJ-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/RKSJ-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Roman.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/Roman.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UCS2-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UCS2-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UCS2-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UCS2-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UTF16-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UTF16-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UTF16-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UTF16-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UTF32-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UTF32-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UTF8-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UTF8-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UTF8-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniCNS-UTF8-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UCS2-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UCS2-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UCS2-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UCS2-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UTF16-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UTF16-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UTF16-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UTF16-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UTF32-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UTF32-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UTF8-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UTF8-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UTF8-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniGB-UTF8-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UCS2-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UCS2-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UCS2-HW-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UCS2-HW-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UCS2-HW-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UCS2-HW-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UCS2-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UCS2-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UTF16-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UTF16-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UTF16-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UTF16-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UTF32-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UTF32-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UTF8-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UTF8-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UTF8-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS-UTF8-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS2004-UTF16-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS2004-UTF16-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS2004-UTF16-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS2004-UTF16-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS2004-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS2004-UTF32-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS2004-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS2004-UTF32-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS2004-UTF8-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS2004-UTF8-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS2004-UTF8-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJIS2004-UTF8-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISPro-UCS2-HW-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISPro-UCS2-HW-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISPro-UCS2-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISPro-UCS2-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISPro-UTF8-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISPro-UTF8-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISX0213-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISX0213-UTF32-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISX0213-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISX0213-UTF32-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISX02132004-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISX02132004-UTF32-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISX02132004-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniJISX02132004-UTF32-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UCS2-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UCS2-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UCS2-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UCS2-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UTF16-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UTF16-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UTF16-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UTF16-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UTF32-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UTF32-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UTF32-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UTF32-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UTF8-H.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UTF8-H.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UTF8-V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/UniKS-UTF8-V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/V.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/V.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/WP-Symbol.bcmap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/cmaps/WP-Symbol.bcmap -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/compressed.tracemonkey-pldi-09.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/compressed.tracemonkey-pldi-09.pdf -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/annotation-check.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/annotation-insert.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/annotation-newparagraph.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/annotation-noicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-next-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-next-rtl.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-next-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-next-rtl@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-next.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-next@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-next@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-previous-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-previous-rtl.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-previous-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-previous-rtl@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-previous.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-previous@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/findbarButton-previous@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/grab.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/grab.cur -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/grabbing.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/grabbing.cur -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/loading-icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/loading-icon.gif -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/loading-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/loading-small.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/loading-small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/loading-small@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-documentProperties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-documentProperties.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-documentProperties@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-documentProperties@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-firstPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-firstPage.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-firstPage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-firstPage@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-handTool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-handTool.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-handTool@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-handTool@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-lastPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-lastPage.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-lastPage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-lastPage@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-rotateCcw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-rotateCcw.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-rotateCcw@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-rotateCcw@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-rotateCw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-rotateCw.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-rotateCw@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-rotateCw@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-scrollHorizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-scrollHorizontal.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-scrollHorizontal@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-scrollHorizontal@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-scrollVertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-scrollVertical.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-scrollVertical@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-scrollVertical@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-scrollWrapped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-scrollWrapped.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-scrollWrapped@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-scrollWrapped@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-selectTool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-selectTool.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-selectTool@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-selectTool@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-spreadEven.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-spreadEven.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-spreadEven@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-spreadEven@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-spreadNone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-spreadNone.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-spreadNone@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-spreadNone@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-spreadOdd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-spreadOdd.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-spreadOdd@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/secondaryToolbarButton-spreadOdd@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/shadow.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/texture.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-bookmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-bookmark.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-bookmark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-bookmark@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-download.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-download@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-download@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-menuArrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-menuArrows.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-menuArrows@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-menuArrows@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-openFile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-openFile.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-openFile@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-openFile@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageDown-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageDown-rtl.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageDown-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageDown-rtl@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageDown.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageDown@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageDown@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageUp-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageUp-rtl.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageUp-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageUp-rtl@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageUp.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageUp@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-pageUp@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-presentationMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-presentationMode.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-presentationMode@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-presentationMode@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-print.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-print.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-print@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-print@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-search.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-search@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-secondaryToolbarToggle-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-secondaryToolbarToggle-rtl.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-secondaryToolbarToggle-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-secondaryToolbarToggle-rtl@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-secondaryToolbarToggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-secondaryToolbarToggle.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-secondaryToolbarToggle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-secondaryToolbarToggle@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-sidebarToggle-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-sidebarToggle-rtl.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-sidebarToggle-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-sidebarToggle-rtl@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-sidebarToggle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-sidebarToggle.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-sidebarToggle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-sidebarToggle@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewAttachments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewAttachments.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewAttachments@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewAttachments@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewOutline-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewOutline-rtl.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewOutline-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewOutline-rtl@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewOutline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewOutline.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewOutline@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewOutline@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewThumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewThumbnail.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewThumbnail@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-viewThumbnail@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-zoomIn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-zoomIn.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-zoomIn@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-zoomIn@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-zoomOut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-zoomOut.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-zoomOut@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/toolbarButton-zoomOut@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/treeitem-collapsed-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/treeitem-collapsed-rtl.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/treeitem-collapsed-rtl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/treeitem-collapsed-rtl@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/treeitem-collapsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/treeitem-collapsed.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/treeitem-collapsed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/treeitem-collapsed@2x.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/treeitem-expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/treeitem-expanded.png -------------------------------------------------------------------------------- /backend/public/third-party/pdfjs-2.4.456-dist/web/images/treeitem-expanded@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/public/third-party/pdfjs-2.4.456-dist/web/images/treeitem-expanded@2x.png -------------------------------------------------------------------------------- /backend/test/resources/META-INF/services/javax.ws.rs.client.ClientBuilder: -------------------------------------------------------------------------------- 1 | org.glassfish.jersey.client.JerseyClientBuilder -------------------------------------------------------------------------------- /backend/test/resources/ingestme.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/test/resources/ingestme.zip -------------------------------------------------------------------------------- /backend/test/resources/ingestme/Georgia_opposition_NATO-Eng-F.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/test/resources/ingestme/Georgia_opposition_NATO-Eng-F.doc -------------------------------------------------------------------------------- /backend/test/resources/ingestme/directory/A hundred years.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/backend/test/resources/ingestme/directory/A hundred years.pdf -------------------------------------------------------------------------------- /backend/test/resources/ingestme/directory/shared.txt: -------------------------------------------------------------------------------- 1 | This is a text file with shared text 2 | -------------------------------------------------------------------------------- /backend/test/resources/ingestme/Справочник/общий.txt: -------------------------------------------------------------------------------- 1 | This is a text file with shared text 2 | -------------------------------------------------------------------------------- /backend/test/resources/ingestme/دليل/اشترك.txt: -------------------------------------------------------------------------------- 1 | This is a text file with shared text 2 | -------------------------------------------------------------------------------- /backend/test/resources/ingestme/ディレクトリ/共有.txt: -------------------------------------------------------------------------------- 1 | This is a text file with shared text 2 | -------------------------------------------------------------------------------- /backend/test/resources/ingestme/目录/共享.txt: -------------------------------------------------------------------------------- 1 | This is a text file with shared text 2 | -------------------------------------------------------------------------------- /backend/test/resources/ingestme/💩/🎁.txt: -------------------------------------------------------------------------------- 1 | This is a text file with shared text 2 | -------------------------------------------------------------------------------- /backend/test/services/TestTypeDetector.scala: -------------------------------------------------------------------------------- 1 | package services 2 | import java.nio.file.Path 3 | 4 | import org.apache.tika.mime.MediaType 5 | import utils.attempt.Failure 6 | 7 | class TestTypeDetector(mimeType: String) extends TypeDetector { 8 | override def detectType(path: Path): Either[Failure, MediaType] = Right(MediaType.parse(mimeType)) 9 | } 10 | -------------------------------------------------------------------------------- /backend/test/test/EmptyAppLoader.scala: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import play.api.{ApplicationLoader, BuiltInComponentsFromContext} 4 | import play.api.routing.Router 5 | 6 | class EmptyAppLoader extends ApplicationLoader { 7 | override def load(context: ApplicationLoader.Context) = { 8 | new BuiltInComponentsFromContext(context) { 9 | override def router = Router.empty 10 | override def httpFilters = Nil 11 | }.application 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/test/test/SomePatience.scala: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import org.scalatest.concurrent.PatienceConfiguration 4 | import org.scalatest.time.{Millis, Seconds, Span} 5 | 6 | trait SomePatience extends PatienceConfiguration { 7 | implicit override val patienceConfig = PatienceConfig(scaled(Span(5, Seconds)), scaled(Span(10, Millis))) 8 | } 9 | -------------------------------------------------------------------------------- /backend/test/test/TestAuthActionBuilder.scala: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import play.api.mvc.Security.AuthenticatedRequest 4 | import play.api.mvc._ 5 | import utils.auth 6 | import utils.auth.{AuthActionBuilder, UserIdentityRequest} 7 | 8 | import scala.concurrent.Future 9 | 10 | class TestAuthActionBuilder(override val controllerComponents: ControllerComponents, reqUser: auth.User) extends AuthActionBuilder { 11 | override def invokeBlock[A](request: Request[A], block: UserIdentityRequest[A] => Future[Result]) = { 12 | block(new AuthenticatedRequest(reqUser, request)) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/test/test/TestPostgresClient.scala: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import services.observability.{BlobMetadata, BlobStatus, IngestionEvent, PostgresClient} 4 | import utils.attempt.Failure 5 | 6 | class TestPostgresClient extends PostgresClient{ 7 | override def insertEvent(event: IngestionEvent): Either[Failure, Unit] = Right(()) 8 | 9 | override def insertMetadata(metaData: BlobMetadata): Either[Failure, Unit] = Right(()) 10 | 11 | def getEvents (ingestId: String, ingestIdIsPrefix: Boolean): Either[Failure, List[BlobStatus]] = Right(List()) 12 | 13 | def deleteBlobIngestionEventsAndMetadata(blobId: String): Either[Failure, Long] = Right(0) 14 | 15 | } 16 | -------------------------------------------------------------------------------- /backend/test/test/TestPreviewService.scala: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import com.amazonaws.util.StringInputStream 4 | import model.{ObjectData, ObjectMetadata, Uri} 5 | import services.previewing.PreviewService 6 | import utils.attempt.Attempt 7 | 8 | class TestPreviewService extends PreviewService { 9 | override def getPreviewType(uri: Uri): Attempt[String] = Attempt.Right("application/pdf") 10 | override def generatePreview(uri: Uri): Attempt[Unit] = Attempt.Right(()) 11 | override def getPreviewObject(uri: Uri): Attempt[ObjectData] = Attempt.Right(ObjectData( 12 | new StringInputStream("Not a real preview"), ObjectMetadata(-1, "application/pdf") 13 | )) 14 | } 15 | -------------------------------------------------------------------------------- /backend/test/test/fixtures/GoogleAuthenticator.scala: -------------------------------------------------------------------------------- 1 | package test.fixtures 2 | 3 | import java.time.{LocalDateTime, ZoneOffset} 4 | 5 | import utils.auth.totp.Base32Secret 6 | import utils.Epoch 7 | 8 | object GoogleAuthenticator { 9 | // these have been eyeballed on the official Google Authenticator app 10 | val sampleSecret = Base32Secret("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ") 11 | val sampleTime = LocalDateTime.of(2017, 12, 5, 11, 5, 0) 12 | val sampleEpoch = Epoch(sampleTime.toEpochSecond(ZoneOffset.UTC)) 13 | val sampleAnswers = List("703365", "849621", "180272", "254200", "247225") 14 | } 15 | -------------------------------------------------------------------------------- /cli/src/main/scala/com/gu/pfi/cli/HashFiles.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pfi.cli 2 | 3 | import java.io.File 4 | 5 | import services.FingerprintServices 6 | import utils.Logging 7 | 8 | object HashFiles extends Logging { 9 | def run(files: Seq[File]): Unit = { 10 | files.foreach { file => 11 | if (file.isFile) { 12 | val fingerprint = FingerprintServices.createFingerprintFromFile(file) 13 | logger.info(s"$fingerprint $file") 14 | } else { 15 | logger.error(s"$file: not a regular file") 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /cli/src/main/scala/com/gu/pfi/cli/model/ConflictBehaviour.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pfi.cli.model 2 | 3 | trait ConflictBehaviour { 4 | def name: String 5 | } 6 | case object Delete extends ConflictBehaviour { 7 | val name = "delete" 8 | } 9 | case object Skip extends ConflictBehaviour { 10 | val name = "skip" 11 | } 12 | case object Stop extends ConflictBehaviour { 13 | val name = "stop" 14 | } 15 | -------------------------------------------------------------------------------- /cli/src/main/scala/com/gu/pfi/cli/model/VerifyIngestionResult.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pfi.cli.model 2 | 3 | case class VerifyIngestionResult( 4 | numberOfFilesOnDisk: Long, 5 | numberOfFilesInIndex: Long, 6 | filesInError: Map[String, String], 7 | filesNotIndexed: List[String] 8 | ) -------------------------------------------------------------------------------- /cli/src/main/scala/com/gu/pfi/cli/service/CliFiles.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pfi.cli.service 2 | 3 | import java.nio.charset.StandardCharsets 4 | import java.nio.file.{Files, Path} 5 | 6 | import utils.attempt.Attempt 7 | 8 | object CliFiles { 9 | def writeFile(file: Path, contents: String): Attempt[Unit] = Attempt.catchNonFatalBlasé { 10 | Files.write(file, contents.getBytes(StandardCharsets.UTF_8)) 11 | } 12 | 13 | def readFile(file: Path): Attempt[String] = Attempt.catchNonFatalBlasé { 14 | new String(Files.readAllBytes(file), StandardCharsets.UTF_8) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cli/src/test/scala/com/gu/pfi/cli/service/CliIngestionServiceTest.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pfi.cli.service 2 | 3 | import java.nio.file.Paths 4 | 5 | import CliIngestionService.relativise 6 | import org.scalatest.funsuite.AnyFunSuite 7 | import org.scalatest.matchers.must.Matchers 8 | 9 | class CliIngestionServiceTest extends AnyFunSuite with Matchers { 10 | test("relativise") { 11 | val root = Paths.get("/Users/dave_benson_phillips") 12 | val gunge = root.resolve("gunge_prices.xlsx") 13 | val bigRedMover = root.resolve("side_projects/big_red_mover/brochure.rtf") 14 | 15 | relativise(root, gunge) must be("dave_benson_phillips/gunge_prices.xlsx") 16 | relativise(root, bigRedMover) must be("dave_benson_phillips/side_projects/big_red_mover/brochure.rtf") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /common/src/main/scala/model/CliCollection.scala: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import play.api.libs.json.{Format, Json} 4 | 5 | case class CliIngestion(uri: String, path: Option[String]) 6 | object CliIngestion { 7 | implicit val format: Format[CliIngestion] = Json.format[CliIngestion] 8 | } 9 | 10 | case class CliCollection(uri: String, ingestions: List[CliIngestion]) 11 | object CliCollection { 12 | implicit val format: Format[CliCollection] = Json.format[CliCollection] 13 | } 14 | -------------------------------------------------------------------------------- /common/src/main/scala/model/CreateCollectionRequest.scala: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import play.api.libs.json.Json 4 | 5 | case class CreateCollectionRequest(name: String) 6 | 7 | object CreateCollectionRequest { 8 | implicit val createCollectionDataFormat = Json.format[CreateCollectionRequest] 9 | } 10 | -------------------------------------------------------------------------------- /common/src/main/scala/model/CreateIngestion.scala: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import play.api.libs.json.{Format, Json} 4 | 5 | // Fixed and default are optional for compatibility with older builds of the cli but default safe to fixed=true, default=false 6 | case class CreateIngestionRequest(path: Option[String], name: Option[String], languages: List[String], fixed: Option[Boolean], default: Option[Boolean]) 7 | 8 | object CreateIngestionRequest { 9 | implicit val format: Format[CreateIngestionRequest] = Json.format[CreateIngestionRequest] 10 | } 11 | 12 | case class CreateIngestionResponse(uri: String, bucket: String, region: String, endpoint: Option[String]) 13 | 14 | object CreateIngestionResponse { 15 | implicit val format: Format[CreateIngestionResponse] = Json.format[CreateIngestionResponse] 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/scala/model/VerifyRequest.scala: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import play.api.libs.json.{Format, Json} 4 | 5 | case class VerifyRequest(files: List[VerifyRequestFile]) 6 | object VerifyRequest { 7 | implicit val format: Format[VerifyRequest] = Json.format[VerifyRequest] 8 | } 9 | 10 | case class VerifyRequestFile(path: String, fingerprint: Option[String]) 11 | object VerifyRequestFile { 12 | implicit val format: Format[VerifyRequestFile] = Json.format[VerifyRequestFile] 13 | } 14 | -------------------------------------------------------------------------------- /common/src/main/scala/model/VerifyResponse.scala: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import play.api.libs.json.{Format, Json} 4 | 5 | case class VerifyResponse(numberOfFilesInIndex: Int, filesNotIndexed: List[String], filesInError: Map[String, String]) 6 | object VerifyResponse { 7 | implicit val format: Format[VerifyResponse] = Json.format[VerifyResponse] 8 | } 9 | -------------------------------------------------------------------------------- /common/src/main/scala/model/index/IndexedBlob.scala: -------------------------------------------------------------------------------- 1 | package model.index 2 | 3 | import play.api.libs.json.{Format, Json} 4 | 5 | case class IndexedBlob(uri: String, collections: Set[String], ingestions: Set[String]) 6 | object IndexedBlob { 7 | implicit val format: Format[IndexedBlob] = Json.format[IndexedBlob] 8 | } 9 | -------------------------------------------------------------------------------- /common/src/main/scala/model/ingestion/OnDiskFileContext.scala: -------------------------------------------------------------------------------- 1 | package model.ingestion 2 | 3 | import java.nio.file.Path 4 | 5 | import model.{Language, Uri} 6 | 7 | case class OnDiskFileContext(file: IngestionFile, parents: List[Uri], ingestion: String, languages: List[Language], path: Path) { 8 | def isRegularFile: Boolean = file.isRegularFile 9 | def size: Long = file.size 10 | } 11 | -------------------------------------------------------------------------------- /common/src/main/scala/model/ingestion/package.scala: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import java.util.UUID 4 | 5 | package object ingestion { 6 | type Key = (Long, UUID) 7 | implicit class RichKey(key: Key) { 8 | def asObjectKey = s"${key._1}_${key._2}" 9 | } 10 | 11 | val dataPrefix = "data/" 12 | val metadataPrefix = "metadata/" 13 | val dataSuffix = ".data" 14 | val metadataSuffix = ".metadata.json" 15 | 16 | def dataKey(key: Key): String = s"$dataPrefix${key.asObjectKey}$dataSuffix" 17 | def metadataKey(key: Key): String = s"$metadataPrefix${key.asObjectKey}$metadataSuffix" 18 | } 19 | -------------------------------------------------------------------------------- /common/src/main/scala/model/user/NewUser.scala: -------------------------------------------------------------------------------- 1 | package model.user 2 | 3 | import play.api.libs.json.Json 4 | 5 | // This is used when an admin creates the skeleton of a user 6 | case class NewUser(username: String, password: String) 7 | 8 | object NewUser { 9 | implicit val newUserFormat = Json.format[NewUser] 10 | } 11 | -------------------------------------------------------------------------------- /common/src/main/scala/utils/IngestionVerification.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | object IngestionVerification { 4 | val BATCH_SIZE = 50 5 | } 6 | -------------------------------------------------------------------------------- /common/src/main/scala/utils/Logging.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import org.slf4j.{Logger, LoggerFactory} 4 | 5 | trait Logging { 6 | val logger: Logger = LoggerFactory.getLogger(this.getClass) 7 | } 8 | -------------------------------------------------------------------------------- /docs/images/admin_quickstart/01_genesis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/admin_quickstart/01_genesis.png -------------------------------------------------------------------------------- /docs/images/admin_quickstart/02_2fa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/admin_quickstart/02_2fa.png -------------------------------------------------------------------------------- /docs/images/admin_quickstart/03_create_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/admin_quickstart/03_create_user.png -------------------------------------------------------------------------------- /docs/images/admin_quickstart/04_user_register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/admin_quickstart/04_user_register.png -------------------------------------------------------------------------------- /docs/images/admin_quickstart/05_user_management.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/admin_quickstart/05_user_management.png -------------------------------------------------------------------------------- /docs/images/giant-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/giant-screenshot.png -------------------------------------------------------------------------------- /docs/images/giant_upload_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/giant_upload_arch.png -------------------------------------------------------------------------------- /docs/images/user_quickstart/01_workspaces_navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/user_quickstart/01_workspaces_navigation.png -------------------------------------------------------------------------------- /docs/images/user_quickstart/02_new_workspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/user_quickstart/02_new_workspace.png -------------------------------------------------------------------------------- /docs/images/user_quickstart/03_upload_to_workspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/user_quickstart/03_upload_to_workspace.png -------------------------------------------------------------------------------- /docs/images/user_quickstart/04_upload_to_workspace_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/user_quickstart/04_upload_to_workspace_dialog.png -------------------------------------------------------------------------------- /docs/images/user_quickstart/05_workspace_processing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/user_quickstart/05_workspace_processing.png -------------------------------------------------------------------------------- /docs/images/user_quickstart/06_uploaded_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/user_quickstart/06_uploaded_file.png -------------------------------------------------------------------------------- /docs/images/user_quickstart/07_search_navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/user_quickstart/07_search_navigation.png -------------------------------------------------------------------------------- /docs/images/user_quickstart/08_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/user_quickstart/08_search.png -------------------------------------------------------------------------------- /docs/images/user_quickstart/09_share_workspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/user_quickstart/09_share_workspace.png -------------------------------------------------------------------------------- /docs/images/user_quickstart/10_new_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/user_quickstart/10_new_folder.png -------------------------------------------------------------------------------- /docs/images/user_quickstart/11_select_files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/user_quickstart/11_select_files.png -------------------------------------------------------------------------------- /docs/images/user_quickstart/12_file_context_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/docs/images/user_quickstart/12_file_context_menu.png -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /frontend/public/favicon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/frontend/public/favicon-192.png -------------------------------------------------------------------------------- /frontend/public/favicon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/frontend/public/favicon-512.png -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Giant", 3 | "name": "Giant", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "favicon-192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "favicon-512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: / -------------------------------------------------------------------------------- /frontend/src/human-date.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'human-date'; 2 | -------------------------------------------------------------------------------- /frontend/src/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/frontend/src/images/favicon.png -------------------------------------------------------------------------------- /frontend/src/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/frontend/src/images/loading.gif -------------------------------------------------------------------------------- /frontend/src/images/masthead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/giant/d3eca8dffdc6854c725cc3365b6c9c1edb22d280/frontend/src/images/masthead.png -------------------------------------------------------------------------------- /frontend/src/js/actions/highlights.ts: -------------------------------------------------------------------------------- 1 | import { HighlightsAction, HighlightsActionType } from '../types/redux/GiantActions'; 2 | 3 | export function setCurrentHighlight( 4 | resourceUri: string, 5 | searchQuery: string, 6 | view: string, 7 | currentHighlight: number, 8 | ): HighlightsAction { 9 | return { 10 | type: HighlightsActionType.UPDATE_HIGHLIGHTS, 11 | resourceUri, 12 | searchQuery, 13 | view, 14 | currentHighlight 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /frontend/src/js/actions/ingestEvents/updateCurrentWorkspace.ts: -------------------------------------------------------------------------------- 1 | import { UrlParamsActionType } from "../../types/redux/GiantActions" 2 | 3 | export function updateCurrentWorkspace(currentWorkspace: string) { 4 | return { 5 | type: UrlParamsActionType.SET_INGESTION_EVENTS_WORKSPACE_IN_URL, 6 | currentWorkspace, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/js/actions/pages/resetPages.ts: -------------------------------------------------------------------------------- 1 | import { ThunkAction } from "redux-thunk"; 2 | import { GiantAction, PagesActionType } from "../../types/redux/GiantActions"; 3 | import { GiantState } from "../../types/redux/GiantState"; 4 | 5 | export function resetPages(): ThunkAction { 6 | return dispatch => { 7 | dispatch({ type: PagesActionType.RESET_PAGES }); 8 | }; 9 | } -------------------------------------------------------------------------------- /frontend/src/js/actions/preferences.js: -------------------------------------------------------------------------------- 1 | export function getPreference(key) { 2 | const prefs = JSON.parse(localStorage.getItem('preferences')); 3 | return prefs[key]; 4 | } 5 | 6 | export function setPreference(key, value) { 7 | const prefs = JSON.parse(localStorage.getItem('preferences')); 8 | prefs[key] = value; 9 | 10 | localStorage.setItem('preferences', JSON.stringify(prefs)); 11 | return prefs; 12 | } 13 | 14 | export function updatePreference(key, value) { 15 | const prefs = setPreference(key, value); 16 | 17 | return dispatch => { 18 | dispatch(setPreferences(prefs)); 19 | }; 20 | } 21 | 22 | function setPreferences(prefs) { 23 | return { 24 | type: 'APP_SET_PREFERENCES', 25 | receivedAt: Date.now(), 26 | preferences: prefs 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/js/actions/resources/clearResource.js: -------------------------------------------------------------------------------- 1 | export function clearResource() { 2 | return dispatch => { 3 | dispatch(() => ({ 4 | type: 'RESOURCE_CLEAR', 5 | receivedAt: Date.now() 6 | })); 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/js/actions/search/clearSearch.js: -------------------------------------------------------------------------------- 1 | export function clearSearch() { 2 | return dispatch => { 3 | dispatch({ 4 | type: 'SEARCH_CLEAR', 5 | receivedAt: Date.now() 6 | }); 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/js/actions/setFilterExpansionState.ts: -------------------------------------------------------------------------------- 1 | import { ExpandedFiltersActionType, ExpandedFiltersAction } from '../types/redux/GiantActions'; 2 | 3 | export function setFilterExpansionState(key: string, isExpanded: boolean): ExpandedFiltersAction { 4 | return { 5 | type: ExpandedFiltersActionType.SET_FILTER_EXPANSION_STATE, 6 | key, 7 | isExpanded 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /frontend/src/js/actions/urlParams/setCurrentHighlight.ts: -------------------------------------------------------------------------------- 1 | import { UrlParamsAction, UrlParamsActionType } from '../../types/redux/GiantActions'; 2 | 3 | export function setCurrentHighlightInUrl(highlight: string): UrlParamsAction { 4 | return { 5 | type: UrlParamsActionType.SET_CURRENT_HIGHLIGHT_IN_URL, 6 | highlight, 7 | }; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /frontend/src/js/actions/urlParams/setViews.ts: -------------------------------------------------------------------------------- 1 | import { UrlParamsAction, UrlParamsActionType } from '../../types/redux/GiantActions'; 2 | 3 | export function setResourceView(view: any): UrlParamsAction { 4 | return { 5 | type: UrlParamsActionType.SET_RESOURCE_VIEW, 6 | view, 7 | }; 8 | } 9 | 10 | export function setDetailsView(view: any): UrlParamsAction { 11 | return { 12 | type: UrlParamsActionType.SET_DETAILS_VIEW, 13 | view, 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/js/actions/users/setUserPermissions.js: -------------------------------------------------------------------------------- 1 | import {setUserPermissionsApi} from '../../services/UserApi'; 2 | import {listUsers} from './listUsers'; 3 | 4 | export function setUserPermissions(username, granted) { 5 | return dispatch => { 6 | return setUserPermissionsApi(username, granted) 7 | .then(() => { 8 | listUsers()(dispatch); 9 | }) 10 | .catch(error => dispatch(errorSetUserPermissions(username, error))); 11 | }; 12 | } 13 | 14 | function errorSetUserPermissions(error, username) { 15 | return { 16 | type: 'APP_SHOW_ERROR', 17 | message: `Failed to set permissions for user ${username}`, 18 | error: error, 19 | receivedAt: Date.now() 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/js/actions/workspaces/setEntryBeingRenamed.ts: -------------------------------------------------------------------------------- 1 | import { TreeEntry } from '../../types/Tree'; 2 | import { WorkspacesAction, WorkspacesActionType } from '../../types/redux/GiantActions'; 3 | import { WorkspaceEntry } from '../../types/Workspaces'; 4 | 5 | export function setEntryBeingRenamed(entry: TreeEntry | null): WorkspacesAction { 6 | return { 7 | type: WorkspacesActionType.SET_ENTRY_BEING_RENAMED, 8 | entry 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/js/actions/workspaces/setFocusedEntry.ts: -------------------------------------------------------------------------------- 1 | import { TreeEntry } from '../../types/Tree'; 2 | import { WorkspacesAction, WorkspacesActionType } from '../../types/redux/GiantActions'; 3 | import { WorkspaceEntry } from '../../types/Workspaces'; 4 | 5 | export function setFocusedEntry(entry: TreeEntry | null): WorkspacesAction { 6 | return { 7 | type: WorkspacesActionType.SET_FOCUSED_ENTRY, 8 | entry 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/js/actions/workspaces/setNodeAsCollapsed.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from '../../types/Tree'; 2 | import { WorkspacesAction, WorkspacesActionType } from '../../types/redux/GiantActions'; 3 | import { WorkspaceEntry } from '../../types/Workspaces'; 4 | 5 | export function setNodeAsCollapsed(node: TreeNode): WorkspacesAction { 6 | return { 7 | type: WorkspacesActionType.SET_NODE_AS_COLLAPSED, 8 | node 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/js/actions/workspaces/setNodeAsExpanded.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from '../../types/Tree'; 2 | import { WorkspacesAction, WorkspacesActionType } from '../../types/redux/GiantActions'; 3 | import { WorkspaceEntry } from '../../types/Workspaces'; 4 | 5 | export function setNodeAsExpanded(node: TreeNode): WorkspacesAction { 6 | return { 7 | type: WorkspacesActionType.SET_NODE_AS_EXPANDED, 8 | node 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/js/actions/workspaces/setSelectedEntries.ts: -------------------------------------------------------------------------------- 1 | import { TreeEntry } from '../../types/Tree'; 2 | import { WorkspacesAction, WorkspacesActionType } from '../../types/redux/GiantActions'; 3 | import { WorkspaceEntry } from '../../types/Workspaces'; 4 | 5 | export function setSelectedEntries(entries: TreeEntry[]): WorkspacesAction { 6 | return { 7 | type: WorkspacesActionType.SET_SELECTED_ENTRIES, 8 | entries 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/js/components/Collections/CollectionsSidebar/CollectionItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {NavLink} from 'react-router-dom'; 4 | 5 | export class CollectionItem extends React.Component { 6 | static propTypes = { 7 | uri: PropTypes.string.isRequired, 8 | display: PropTypes.string.isRequired 9 | } 10 | 11 | render() { 12 | return ( 13 | 14 |
15 | {this.props.display} 16 |
17 |
18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/js/components/DownloadLink/DownloadLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {authorizedDownload} from '../../services/AuthApi'; 4 | 5 | export class DownloadLink extends React.Component { 6 | static propTypes = { 7 | href: PropTypes.string.isRequired, 8 | className: PropTypes.string, 9 | children: PropTypes.node 10 | }; 11 | 12 | onClick = () => { 13 | authorizedDownload(this.props.href).then(target => window.location.href = target); 14 | }; 15 | 16 | render() { 17 | return ( 18 | 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /frontend/src/js/components/DownloadLink/index.js: -------------------------------------------------------------------------------- 1 | export {DownloadLink} from './DownloadLink'; -------------------------------------------------------------------------------- /frontend/src/js/components/IngestionEvents/IngestionEvents.module.css: -------------------------------------------------------------------------------- 1 | 2 | .dropdownMenus { 3 | display: 'inline-flex'; 4 | flexWrap: 'wrap'; 5 | padding: 2px; 6 | } 7 | 8 | .dropdown { 9 | min-width: 300px; 10 | } 11 | 12 | .expandedRowExtractorStatus { 13 | margin-left: 20px; 14 | } -------------------------------------------------------------------------------- /frontend/src/js/components/IngestionEvents/updateCurrentCollection.ts: -------------------------------------------------------------------------------- 1 | import { UrlParamsActionType } from "../../types/redux/GiantActions" 2 | 3 | export function updateCurrentCollection(currentCollection: string) { 4 | return { 5 | type: UrlParamsActionType.SET_INGESTION_EVENTS_COLLECTION_IN_URL, 6 | currentCollection, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/js/components/IngestionEvents/updateCurrentIngestion.ts: -------------------------------------------------------------------------------- 1 | import { UrlParamsActionType } from "../../types/redux/GiantActions" 2 | 3 | export function updateCurrentIngestion(currentIngestion: string) { 4 | return { 5 | type: UrlParamsActionType.SET_INGESTION_EVENTS_INGESTION_IN_URL, 6 | currentIngestion, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/js/components/PageViewer/Controls.module.css: -------------------------------------------------------------------------------- 1 | .bar { 2 | display: flex; 3 | flex-direction: row; 4 | justify-content: flex-end; 5 | background: var(--color-primary); 6 | padding: 6px; 7 | gap: 6px; 8 | 9 | } 10 | 11 | .bar button { 12 | padding: 1px 4px; 13 | border: 2px; 14 | border-radius: 4px; 15 | margin: 1px; 16 | background-color: var(--color-secondary); 17 | } -------------------------------------------------------------------------------- /frontend/src/js/components/PageViewer/Page.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | position: relative; 3 | } 4 | 5 | .abortError { 6 | position: absolute; 7 | left: 50%; 8 | top: 50%; 9 | transform: translate(-50%,-50%); 10 | } -------------------------------------------------------------------------------- /frontend/src/js/components/PageViewer/PageOverlayText.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import { PdfText } from "./model"; 3 | 4 | type PageOverlayTextProps = { 5 | text: PdfText; 6 | }; 7 | 8 | // This renders invisible text over the PDF to allow selection 9 | // Selection is useful for copy-paste and making comments 10 | export const PageOverlayText: FC = ({ text }) => { 11 | const { left, top, fontSize, fontFamily, transform, value } = text; 12 | 13 | return ( 14 |
18 | {value} 19 |
20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /frontend/src/js/components/PageViewer/PageViewer.module.css: -------------------------------------------------------------------------------- 1 | .main { 2 | height: calc(100vh - 80px); 3 | flex-grow: 1; 4 | display: flex; 5 | position: relative; 6 | } 7 | 8 | .controls { 9 | position: absolute; 10 | right: 20px; 11 | top: 0px; 12 | z-index: 1000; 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/js/components/PageViewer/VirtualScroll.module.css: -------------------------------------------------------------------------------- 1 | .scrollContainer { 2 | width: 100%; 3 | height: 100%; 4 | overflow: auto; 5 | } 6 | 7 | .pages { 8 | position: relative; 9 | display: flex; 10 | flex-direction: column; 11 | align-items: center; 12 | } 13 | 14 | .pageContainer { 15 | position: absolute; 16 | 17 | margin: 10px; 18 | 19 | min-width: 1000px; 20 | min-height: 1000px; 21 | max-width: 10000px; 22 | max-height: 10000px; 23 | 24 | display: flex; 25 | align-items: center; 26 | justify-content: center; 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/js/components/Settings/FileTypes/MimeTypeProgress.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | type MimeTypeProgressProps = { 4 | done: number, 5 | todo: number, 6 | failed: number, 7 | } 8 | 9 | export function MimeTypeProgress({ done, todo, failed }: MimeTypeProgressProps) { 10 | const total = done + todo + failed; 11 | const successWidth = (done / total) * 100; 12 | const failureWidth = (failed / total) * 100; 13 | 14 | return
15 |
16 |
17 |
; 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/js/components/UtilComponents/KeyboardShortcut.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import Mousetrap from 'mousetrap'; 3 | 4 | // We used to use react-keydown that provides a decorator syntax: 5 | // @keydown('a') 6 | // function doYourThing() { } 7 | // 8 | // Decorator syntax is not a standard and therefore not supported by create-react-app and others. 9 | // 10 | // Representing the API using a dummy React node is one way to automatically register/unregister 11 | // the listeners as components appear and disappear. 12 | export function KeyboardShortcut({ shortcut, func }) { 13 | useEffect(() => { 14 | Mousetrap.bind(shortcut, func); 15 | 16 | return () => { 17 | Mousetrap.unbind(shortcut); 18 | }; 19 | }); 20 | 21 | return null; 22 | } -------------------------------------------------------------------------------- /frontend/src/js/components/UtilComponents/MenuChevron.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactSVGElement } from 'react'; 2 | import ChevronIcon from 'react-icons/lib/md/expand-more'; 3 | 4 | type MenuChevronPropTypes = { 5 | expanded: boolean, 6 | onClick: (e: React.MouseEvent) => void 7 | } 8 | 9 | export const MenuChevron = (props: MenuChevronPropTypes) => 10 | ; 14 | -------------------------------------------------------------------------------- /frontend/src/js/components/UtilComponents/ProgressAnimation.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function ProgressAnimation() { 4 | return
5 |
6 |
7 |
8 |
; 9 | } -------------------------------------------------------------------------------- /frontend/src/js/components/UtilComponents/ProgressBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | function percentageWidth(total, value) { 5 | return `${(value / total) * 100}%`; 6 | } 7 | 8 | export const ProgressBar = (props) => ( 9 |
10 |
11 |
12 | ); 13 | 14 | ProgressBar.propTypes = { 15 | highest: PropTypes.number.isRequired, 16 | value: PropTypes.number.isRequired, 17 | className: PropTypes.string 18 | }; -------------------------------------------------------------------------------- /frontend/src/js/components/workspace/WorkspacePublicInfoIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Popup } from 'semantic-ui-react'; 2 | import InfoIcon from 'react-icons/lib/md/info-outline'; 3 | import React from 'react'; 4 | 5 | export function WorkspacePublicInfoIcon() { 6 | const publicExplainer = 7 | 'Public workspaces can be viewed and edited by all Giant users. ' + 8 | 'They are not accessible to people who do not have access to Giant.'; 9 | 10 | return 12 | }/> 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/js/components/workspace/WorkspacePublicMessage.tsx: -------------------------------------------------------------------------------- 1 | import { Message } from 'semantic-ui-react'; 2 | import React from 'react'; 3 | 4 | export function WorkspacePublicMessage() { 5 | return 6 | This workspace is public. 7 |

Anyone with a login to Giant will be able to view, add, move, remove and rename files within it.

8 |

Only you will be able to rename, delete or share the workspace itself.

9 |

You can change this setting at any time by clicking Share Workspace.

10 |
11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/js/eui-components/GiantEuiApp.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route } from 'react-router-dom'; 3 | import GiantEuiHeader from './GiantEuiHeader'; 4 | import GiantEuiSettings from './GiantEuiSettings'; 5 | import GiantEuiWorkspace from './GiantEuiWorkspace'; 6 | import { headerHeight } from './displayConstants'; 7 | 8 | export default function GiantEuiApp() { 9 | 10 | return 11 | 12 |
13 | 14 | 15 |
16 |
; 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/js/eui-components/displayConstants.ts: -------------------------------------------------------------------------------- 1 | export const headerHeight = '49px'; 2 | -------------------------------------------------------------------------------- /frontend/src/js/module.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'human-date'; -------------------------------------------------------------------------------- /frontend/src/js/reducers/clusterReducer.js: -------------------------------------------------------------------------------- 1 | export default function cluster(state = { 2 | nodes: undefined 3 | }, action) { 4 | switch (action.type) { 5 | case 'NODES_GET_RECEIVE': 6 | action.nodes.sort((a, b) => a.hostname.localeCompare(b.hostname)); 7 | return Object.assign({}, state, { 8 | nodes: action.nodes 9 | }); 10 | 11 | default: 12 | return state; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/js/reducers/collectionsReducer.js: -------------------------------------------------------------------------------- 1 | import * as R from 'ramda'; 2 | 3 | export default function collections(state = [], action) { 4 | switch (action.type) { 5 | case 'COLLECTIONS_GET_RECEIVE': 6 | return action.collections; 7 | 8 | case 'COLLECTION_GET_RECEIVE': { 9 | const currentIndex = state.findIndex(c => c.uri === action.collection.uri); 10 | 11 | if (currentIndex !== -1) { 12 | return R.update(currentIndex, action.collection, state); 13 | } 14 | 15 | return state.concat(action.collection); 16 | } 17 | 18 | default: 19 | return state; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/js/reducers/emailsReducer.js: -------------------------------------------------------------------------------- 1 | export default function emails(state = null, action) { 2 | switch (action.type) { 3 | case 'EMAIL_THREAD_RECEIVE': 4 | return Object.assign({}, state, { 5 | uri: action.uri, 6 | timeline: action.timeline 7 | }); 8 | 9 | default: 10 | return state; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/js/reducers/expandedFiltersReducer.ts: -------------------------------------------------------------------------------- 1 | import { ExpandedFiltersAction } from '../types/redux/GiantActions'; 2 | import { ExpandedFiltersState } from '../types/redux/GiantState'; 3 | 4 | export default function expandedFilters(state: ExpandedFiltersState = {}, action: ExpandedFiltersAction): ExpandedFiltersState { 5 | switch (action.type) { 6 | case 'SET_FILTER_EXPANSION_STATE': 7 | return { ...state, ...{ [action.key]: action.isExpanded }}; 8 | 9 | default: 10 | return state; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/js/reducers/filtersReducer.js: -------------------------------------------------------------------------------- 1 | export default function filters(state = [], action) { 2 | switch (action.type) { 3 | case 'FILTERS_GET_RECEIVE': 4 | return action.filters || false; 5 | 6 | default: 7 | return state; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/js/reducers/isLoadingResourceReducer.ts: -------------------------------------------------------------------------------- 1 | import { LoadingState } from '../types/redux/GiantState'; 2 | import { LoadingStateAction, LoadingStateActionType } from '../types/redux/GiantActions'; 3 | 4 | export default function isLoadingResource(state = false, action: LoadingStateAction): LoadingState { 5 | switch (action.type) { 6 | case LoadingStateActionType.SET_RESOURCE_LOADING_STATE: 7 | return action.isLoadingResource; 8 | default: 9 | return state; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/js/reducers/resourceReducer.ts: -------------------------------------------------------------------------------- 1 | import { ResourceAction, ResourceActionType } from "../types/redux/GiantActions"; 2 | import { Resource } from "../types/Resource"; 3 | 4 | export default function resource(state: Resource | null = null, action: ResourceAction): Resource | null { 5 | switch (action.type) { 6 | case ResourceActionType.GET_RECEIVE: 7 | return action.resource; 8 | 9 | case ResourceActionType.RESET_RESOURCE: 10 | return null; 11 | 12 | case ResourceActionType.SET_COMMENTS: 13 | return {...state!, comments: action.comments }; 14 | 15 | case ResourceActionType.SET_SELECTION: 16 | return {...state!, selection: action.selection}; 17 | 18 | default: 19 | return state; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/js/services/ClusterApi.js: -------------------------------------------------------------------------------- 1 | import authFetch from '../util/auth/authFetch'; 2 | 3 | export function fetchNodes() { 4 | return authFetch('/api/cluster/members').then(res => res.json()); 5 | } 6 | 7 | export function listDirectory(hostname, path) { 8 | return authFetch(`/api/cluster/members/${hostname}/fs${path}`).then(res => res.json()); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /frontend/src/js/services/CommentsApi.ts: -------------------------------------------------------------------------------- 1 | import authFetch from '../util/auth/authFetch'; 2 | import { CommentAnchor } from '../types/Resource'; 3 | 4 | export function postComment(uri: string, text: string, anchor?: CommentAnchor) { 5 | return authFetch(`/api/resources/${uri}/comments`, { 6 | headers: new Headers({'Content-Type': 'application/json'}), 7 | method: 'POST', 8 | body: JSON.stringify({text, anchor}) 9 | }); 10 | } 11 | 12 | export function fetchComments(uri: string) { 13 | return authFetch(`/api/resources/${uri}/comments`).then(r => r.json()) 14 | } 15 | 16 | export function deleteComment(commentId: string) { 17 | return authFetch(`/api/comments/${commentId}`, { 18 | method: "DELETE" 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/js/services/DocumentApi.js: -------------------------------------------------------------------------------- 1 | import authFetch from '../util/auth/authFetch'; 2 | 3 | export function fetchIngestions(collectionName) { 4 | return authFetch(`/api/collections/${collectionName}`).then(res => res.json()); 5 | } 6 | 7 | export function authDownloadLink(uri, filename) { 8 | if (filename) { 9 | return `/api/download/auth/${uri}?filename=${filename}`; 10 | } else { 11 | return `/api/download/auth/${uri}`; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/js/services/EmailApi.js: -------------------------------------------------------------------------------- 1 | import authFetch from '../util/auth/authFetch'; 2 | 3 | export function getEmailThread(uri) { 4 | return authFetch(`/api/emails/thread/${uri}`).then(res => res.json()); 5 | } -------------------------------------------------------------------------------- /frontend/src/js/services/FiltersApi.js: -------------------------------------------------------------------------------- 1 | import authFetch from '../util/auth/authFetch'; 2 | 3 | export function fetchFilters() { 4 | return authFetch('/api/filters').then(res => res.json()); 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/js/services/ResourceApi.ts: -------------------------------------------------------------------------------- 1 | import authFetch from '../util/auth/authFetch'; 2 | import { objectToParamString } from '../util/UrlParameters'; 3 | import { Resource } from '../types/Resource'; 4 | 5 | export function fetchResource(uri: string, basic: boolean, highlightQuery?: string): Promise { 6 | const params = highlightQuery ? { basic, q: highlightQuery } : { basic }; 7 | 8 | return authFetch(`/api/resources/${uri}?${objectToParamString(params)}`).then(res => res.json()); 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/js/types/Auth.ts: -------------------------------------------------------------------------------- 1 | import { PartialUser } from './User'; 2 | export type Auth = { 3 | header: string, 4 | requesting: boolean, 5 | require2fa: boolean, 6 | requirePanda: boolean, 7 | forbidden: boolean, 8 | errors: string[], 9 | jwtToken: string, 10 | token?: Token 11 | } 12 | 13 | 14 | type Token = { 15 | exp: number, 16 | user: PartialUser, 17 | issuedAt: number, 18 | refreshedAt: number, 19 | loginExpiry: number, 20 | verificationExpiry: number, 21 | permissions: { 22 | granted: string[] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/js/types/Cluster.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | export const node = PropTypes.shape({ 4 | hostname: PropTypes.string.isRequired, 5 | reachable: PropTypes.bool.isRequired 6 | }); 7 | 8 | export const cluster = PropTypes.shape({ 9 | nodes: PropTypes.arrayOf(node) 10 | }); 11 | 12 | export const fileEntry = PropTypes.shape({ 13 | path: PropTypes.string.isRequired, 14 | fileName: PropTypes.string.isRequired, 15 | isDirectory: PropTypes.bool.isRequired, 16 | children: PropTypes.arrayOf(fileEntry) 17 | }); 18 | -------------------------------------------------------------------------------- /frontend/src/js/types/Config.ts: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | export const config = PropTypes.shape({ 4 | warning: PropTypes.string, 5 | userProvider: PropTypes.string.isRequired, 6 | authConfig: PropTypes.object.isRequired, 7 | buildInfo: PropTypes.object.isRequired 8 | }); 9 | 10 | export type Config = { 11 | warning?: string, 12 | userProvider: string, 13 | hideDownloadButton: boolean, 14 | authConfig: { 15 | require2FA: boolean, 16 | minPasswordLength: number 17 | }, 18 | buildInfo: { 19 | [info: string]: string 20 | } 21 | }; 22 | 23 | 24 | -------------------------------------------------------------------------------- /frontend/src/js/types/ExtractionFailures.ts: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import { BasicResource } from './Resource'; 3 | 4 | export interface ExtractionFailureSummary { 5 | extractorName: string, 6 | stackTrace: string, 7 | numberOfBlobs: number 8 | } 9 | 10 | // case class ResourcesForExtractionFailure(hits: Long, page: Long, pageSize: Long, results: List[BasicResource]) extends Paging[Resource] 11 | export interface ResourcesForExtractionFailure { 12 | hits: number, 13 | page: number, 14 | pageSize: number, 15 | results: BasicResource[] 16 | } 17 | 18 | export interface ExtractionFailures { 19 | results: ExtractionFailureSummary[], 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/js/types/Match.ts: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | export type Match = { 4 | params: { 5 | uri: string, 6 | timestamp: string, 7 | id: string 8 | } 9 | } 10 | 11 | export const match = PropTypes.shape({ 12 | params: PropTypes.shape({ 13 | uri: PropTypes.string, 14 | timestamp: PropTypes.string 15 | }).isRequired 16 | }); 17 | -------------------------------------------------------------------------------- /frontend/src/js/types/Query.ts: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | export type Query = { 4 | id: string, 5 | displayName: string, 6 | query: string, 7 | count: number, 8 | } 9 | 10 | export const query = PropTypes.shape({ 11 | id: PropTypes.string.isRequired, 12 | displayName: PropTypes.string.isRequired, 13 | query: PropTypes.string.isRequired, 14 | count: PropTypes.number.isRequired 15 | }); 16 | -------------------------------------------------------------------------------- /frontend/src/js/types/SearchFilter.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | function lazyFunction(f) { 4 | return function() { 5 | return f.apply(this, arguments); 6 | }; 7 | } 8 | 9 | export const searchFilterOption = PropTypes.shape({ 10 | value: PropTypes.string.isRequired, 11 | display: PropTypes.string.isRequired, 12 | explanation: PropTypes.string, 13 | suboptions: PropTypes.arrayOf(lazyFunction(() => searchFilterOption)) 14 | }); 15 | 16 | export const searchFilter = PropTypes.shape({ 17 | key: PropTypes.string.isRequired, 18 | display: PropTypes.string, 19 | options: PropTypes.arrayOf(searchFilterOption) 20 | }); 21 | -------------------------------------------------------------------------------- /frontend/src/js/types/SuggestedFields.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | export const suggestedFieldsPropType = PropTypes.shape({ 4 | name: PropTypes.string.isRequired, 5 | type: PropTypes.string.isRequired 6 | }); 7 | -------------------------------------------------------------------------------- /frontend/src/js/types/Token.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | export const token = PropTypes.shape({ 4 | issuedAt: PropTypes.number.isRequired, 5 | refreshedAt: PropTypes.number.isRequired, 6 | exp: PropTypes.number.isRequired 7 | }); 8 | -------------------------------------------------------------------------------- /frontend/src/js/types/WizardSlide.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | export const WizardSlide = PropTypes.shape({ 4 | title: PropTypes.string.isRequired, 5 | slide: PropTypes.element.isRequired, 6 | validate: PropTypes.func 7 | }); 8 | -------------------------------------------------------------------------------- /frontend/src/js/types/redux/GiantDispatch.ts: -------------------------------------------------------------------------------- 1 | import { GiantState } from './GiantState'; 2 | import { GiantAction } from './GiantActions'; 3 | import { ThunkDispatch } from 'redux-thunk'; 4 | import { Action } from 'redux'; 5 | 6 | export type GiantDispatch = ThunkDispatch 7 | -------------------------------------------------------------------------------- /frontend/src/js/util/commentUtils.ts: -------------------------------------------------------------------------------- 1 | import { CommentAnchor, CommentData } from "../types/Resource"; 2 | 3 | function commentInView(anchor: CommentAnchor, view?: string): boolean { 4 | if(!view) { 5 | return false; 6 | } 7 | 8 | if(view.startsWith('ocr')) { 9 | const language = view.split(".")[1]; 10 | return anchor.type === 'ocr' && anchor.language === language; 11 | } 12 | 13 | return anchor.type === 'text'; 14 | } 15 | 16 | export function filterCommentsInView(comments: CommentData[], view?: string): CommentData[] { 17 | return comments.filter(({ anchor }) => { 18 | return anchor && commentInView(anchor, view); 19 | }); 20 | } -------------------------------------------------------------------------------- /frontend/src/js/util/history.js: -------------------------------------------------------------------------------- 1 | import { createBrowserHistory } from 'history'; 2 | 3 | export default createBrowserHistory(); 4 | -------------------------------------------------------------------------------- /frontend/src/js/util/isLoggedIn.js: -------------------------------------------------------------------------------- 1 | export function isLoggedIn(auth) { 2 | const d = new Date(); 3 | const seconds = Math.round(d.getTime() / 1000); 4 | return auth.token !== undefined && // have token 5 | auth.token.exp !== undefined && auth.token.exp > seconds && // token has been renewed recently 6 | auth.token.loginExpiry !== undefined && auth.token.loginExpiry > seconds; // last log in was recent enough 7 | } 8 | -------------------------------------------------------------------------------- /frontend/src/js/util/keyboardShortcuts.js: -------------------------------------------------------------------------------- 1 | export const keyboardShortcuts = { 2 | previousResult: 'shift+left', 3 | nextResult: 'shift+right', 4 | previousHighlight: 'shift+n', 5 | nextHighlight: 'n', 6 | focusSearchBox: 's', 7 | showText: 'shift+x', 8 | showPreview: 'shift+c' 9 | }; 10 | -------------------------------------------------------------------------------- /frontend/src/js/util/markdownToHtml.js: -------------------------------------------------------------------------------- 1 | // Very limited markdown converter, useful for tooltips 2 | export default function markdownToHtml(markdown) { 3 | var html = markdown; 4 | var count = (html.match(/`/g) || []).length; 5 | while(count > 1) { 6 | html = html.replace('`', ''); 7 | html = html.replace('`', ''); 8 | count = (html.match(/`/g) || []).length; 9 | } 10 | return html; 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/js/util/markdownToHtml.spec.js: -------------------------------------------------------------------------------- 1 | import markdownToHtml from './markdownToHtml'; 2 | 3 | test('correctly convert with no backticks', () => { 4 | expect(markdownToHtml('test text no backticks')).toBe('test text no backticks'); 5 | }); 6 | 7 | test('correctly convert with one backtick', () => { 8 | expect(markdownToHtml('test `text one backtick')).toBe('test `text one backtick'); 9 | }); 10 | 11 | test('correctly convert with two backticks', () => { 12 | expect(markdownToHtml('test `text` two backticks')).toBe('test text two backticks'); 13 | }); 14 | 15 | test('correctly convert with three backticks', () => { 16 | expect(markdownToHtml('test `text` three `backticks')).toBe('test text three `backticks'); 17 | }); 18 | -------------------------------------------------------------------------------- /frontend/src/js/util/readableFileSize.js: -------------------------------------------------------------------------------- 1 | export function readableFileSize(bytes, fractionalDigits = 1) { 2 | const threshold = 1024; 3 | const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB']; 4 | 5 | if (bytes < 0 || bytes > Math.pow(threshold, units.length)) { 6 | throw new Error(`Cannot convert to human readable bytes, value is out of range (${bytes}).`); 7 | } 8 | 9 | let unitIdx = 0; 10 | 11 | while (bytes >= threshold) { 12 | bytes /= threshold; 13 | unitIdx += 1; 14 | } 15 | 16 | return bytes.toFixed(fractionalDigits) + ' ' + units[unitIdx]; 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/js/util/regexEscape.js: -------------------------------------------------------------------------------- 1 | export default function(s) { 2 | return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/js/util/stringUtils.spec.ts: -------------------------------------------------------------------------------- 1 | import { getLastPart } from './stringUtils'; 2 | 3 | test('getLastPart', () => { 4 | expect(getLastPart('blah/blurgh/hey', '/')).toBe('hey'); 5 | expect(getLastPart('ha', '/')).toBe('ha'); 6 | expect(getLastPart('hey.blurgh/hey', '.')).toBe('blurgh/hey'); 7 | }); 8 | -------------------------------------------------------------------------------- /frontend/src/js/util/stringUtils.ts: -------------------------------------------------------------------------------- 1 | export function getLastPart(input: string, separator: string) { 2 | return input.split(separator).slice(-1)[0]; 3 | } 4 | 5 | export function removeLastUnmatchedQuote(input: string) { 6 | const quoteCount = [...input.matchAll(/"/g)].length; 7 | if ((quoteCount % 2) !== 0) { 8 | return input.replace(/(.*)(")(.*)$/, '$1$3'); 9 | } 10 | return input; 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/js/util/styleLocalization.js: -------------------------------------------------------------------------------- 1 | import i18n from '../i18n'; 2 | 3 | export default function directionalStyle(baseClass) { 4 | if (i18n.dir(i18n.language) == 'rtl') { 5 | return baseClass + '--rtl'; 6 | } 7 | return baseClass; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/js/util/stylesheets/EUIStyles.tsx: -------------------------------------------------------------------------------- 1 | import '@elastic/eui/dist/eui_theme_light.css'; 2 | import '../../../stylesheets/eui-main.scss'; 3 | 4 | const EUIStyles: React.FC = () => null; 5 | export default EUIStyles; -------------------------------------------------------------------------------- /frontend/src/js/util/stylesheets/OriginalGiantStyles.tsx: -------------------------------------------------------------------------------- 1 | import 'semantic-ui-css/semantic.min.css'; 2 | import '../../../stylesheets/main.scss'; 3 | 4 | const OriginalGiantStyles: React.FC = () => null; 5 | export default OriginalGiantStyles; -------------------------------------------------------------------------------- /frontend/src/js/util/stylesheets/StylesheetLoader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const OriginalGiantStyles = React.lazy(() => import('./OriginalGiantStyles')); 4 | const EUIStyles = React.lazy(() => import('./EUIStyles')); 5 | 6 | // This is a trick to conditionally load CSS at runtime without ejecting from Create React App 7 | // https://stackoverflow.com/questions/46835825/conditional-css-in-create-react-app 8 | export function StylesheetLoader({ eui, children }: { eui: boolean, children: React.ReactElement[] }) { 9 | return 10 | Loading...}> 11 | {eui && } 12 | {!eui && } 13 | {children} 14 | 15 | 16 | } -------------------------------------------------------------------------------- /frontend/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /frontend/src/setupProxy.js: -------------------------------------------------------------------------------- 1 | const { createProxyMiddleware } = require('http-proxy-middleware'); 2 | 3 | // We must manually configure our own proxy as the default behaviour 4 | // (proxy everything without `Accept: text-html`) breaks PDF preview 5 | module.exports = function(app) { 6 | app.use('/api', createProxyMiddleware({ target: 'http://localhost:9001/api', changeOrigin: true })); 7 | app.use('/setup', createProxyMiddleware({ target: 'http://localhost:9001/setup', changeOrigin: true })); 8 | app.use('/third-party', createProxyMiddleware({ target: 'http://localhost:9001/third-party', changeOrigin: true })); 9 | }; -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_bignum.scss: -------------------------------------------------------------------------------- 1 | .bignum-container { 2 | display: flex; 3 | justify-content: space-evenly; 4 | } 5 | 6 | .bignum { 7 | text-align: center; 8 | } 9 | 10 | .bignum__number { 11 | font-size: 2rem; 12 | font-weight: bold; 13 | } -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_comments.scss: -------------------------------------------------------------------------------- 1 | .comments-sidebar { 2 | min-width: $sidebarWidth; 3 | margin-right: 30px; 4 | position: relative; 5 | } 6 | 7 | .comment { 8 | position: absolute; 9 | padding: $baseSpacing; 10 | background: $secondary; 11 | width: 100%; 12 | /* stretch to fill width */ 13 | left: 0; 14 | right: 0; 15 | } 16 | 17 | .comment--animatable { 18 | transition: top .4s ease 0s, left .4s ease 0s; 19 | } 20 | 21 | .comment--add { 22 | border-top: 1px solid $borderColour; 23 | background: $primary; 24 | padding: $baseSpacing; 25 | } 26 | 27 | .comment--add > textarea { 28 | resize: none; 29 | width: 100%; 30 | margin-bottom: $baseSpacing; 31 | } -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_coverage.scss: -------------------------------------------------------------------------------- 1 | // COntains widths for the mimetype table columns 2 | .mime-coverage { 3 | td:nth-child(2) { 4 | width: 30%; 5 | } 6 | 7 | td:nth-child(3) { 8 | width: 10%; 9 | } 10 | } -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_directory.scss: -------------------------------------------------------------------------------- 1 | .directory { 2 | 3 | &__item { 4 | display: block; 5 | padding: $baseSpacing 0; 6 | 7 | &:not(:last-child) { 8 | border-bottom: 1px solid $borderColour; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_email.scss: -------------------------------------------------------------------------------- 1 | .email__header { 2 | margin: 10px 30px; 3 | border-bottom: 1px solid $borderColour; 4 | } 5 | 6 | .email__header-row { 7 | display: flex; 8 | align-items: center; 9 | flex-wrap: wrap; 10 | } 11 | 12 | .email__header-item { 13 | margin-left: 10px; 14 | } 15 | 16 | .email__header-item--inline { 17 | display: flex; 18 | flex-wrap: wrap; 19 | } 20 | 21 | .email__recipient { 22 | margin-right: 10px; 23 | } 24 | 25 | .email__recipient-address { 26 | margin-left: 5px; 27 | font-size: 12px; 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_error-bar.scss: -------------------------------------------------------------------------------- 1 | .error-bar { 2 | display: flex; 3 | position: absolute; 4 | align-items: left; 5 | margin: 5px; 6 | flex-direction: column; 7 | z-index: 100; 8 | } 9 | 10 | .error-bar__text { 11 | margin: 5px; 12 | } 13 | 14 | .error-bar__icon { 15 | height: 100%; 16 | margin: 5px; 17 | 18 | &:hover { 19 | cursor: pointer; 20 | } 21 | } 22 | 23 | .error-bar__item { 24 | margin: 5px; 25 | border-radius: 5px; 26 | display: flex; 27 | justify-content: space-between; 28 | align-items: center; 29 | } 30 | 31 | .error-bar__error { 32 | color: white; 33 | background-color: $errorColour; 34 | } 35 | 36 | .error-bar__warning { 37 | color: $primary; 38 | background-color: $warningColour; 39 | } 40 | -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_help.scss: -------------------------------------------------------------------------------- 1 | .help { 2 | height: calc(100vh - 100px); 3 | overflow: auto; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_hover-search-link.scss: -------------------------------------------------------------------------------- 1 | .hover-search-link { 2 | position: relative; 3 | } 4 | 5 | .hover-search-link__icon-wrapper--left { 6 | position: absolute; 7 | left: -30px; 8 | top: -5px; 9 | z-index: 10; 10 | } 11 | 12 | .hover-search-link__icon-wrapper { 13 | position: absolute; 14 | right: -35px; 15 | top: -5px; 16 | z-index: 10; 17 | } 18 | 19 | .hover-search-link__icon { 20 | color: $primaryDark; 21 | font-size: 30px; 22 | font-weight: bold; 23 | } 24 | 25 | .hover-search-link__icon--highlighted { 26 | color: $highlightColour; 27 | font-size: 30px; 28 | font-weight: bold; 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_lazy-tree-browser.scss: -------------------------------------------------------------------------------- 1 | .lazy-tree-browser__filename { 2 | margin: 10px 0; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_modal-action.scss: -------------------------------------------------------------------------------- 1 | .modal-action__modal { 2 | width: 100%; 3 | padding: $baseSpacing; 4 | } 5 | 6 | .modal-action__panel { 7 | padding: $baseSpacing; 8 | } 9 | 10 | .modal-action__modal-text { 11 | margin-left: $baseSpacing; 12 | } 13 | 14 | .modal-action__buttons { 15 | margin-top: $baseSpacing; 16 | float: right; 17 | 18 | & button { 19 | margin-right: $baseSpacing; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_page-navigator.scss: -------------------------------------------------------------------------------- 1 | .page-navigator { 2 | margin: $baseSpacing; 3 | text-align: center; 4 | } 5 | 6 | .page-navigator__page { 7 | color: $primaryDark; 8 | display: inline-block; 9 | height: 30px; 10 | min-width: 30px; 11 | line-height: 30px; 12 | border-radius: 50%; 13 | 14 | &:not(:last-child) { 15 | margin-right: 4px; 16 | } 17 | 18 | &:hover:not(.page-navigator__page--selected) { 19 | cursor: pointer; 20 | text-decoration: none; 21 | background-color: $primary; 22 | color: white; 23 | } 24 | 25 | &--selected { 26 | font-weight: bold; 27 | color: white; 28 | background-color: $primaryDark; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_progress-bar.scss: -------------------------------------------------------------------------------- 1 | %progress-bar { 2 | display: inline-block; 3 | width: 100%; 4 | height: 12px; 5 | } 6 | 7 | %progress-bar__bar { 8 | background-color: white; 9 | height: 100%; 10 | 11 | transition: width 0.5s; 12 | } 13 | 14 | .progress-bar { 15 | @extend %progress-bar; 16 | 17 | &__bar { 18 | @extend %progress-bar__bar; 19 | } 20 | } 21 | 22 | .progress-bar--highlight { 23 | @extend %progress-bar; 24 | 25 | .progress-bar__bar { 26 | @extend %progress-bar__bar; 27 | background-color: $highlightColour; 28 | } 29 | } -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_resource-browser.scss: -------------------------------------------------------------------------------- 1 | .resource-browser { 2 | padding: $baseSpacing 0; 3 | 4 | &__path { 5 | font-size: 14px; 6 | font-weight: bold; 7 | } 8 | 9 | &__resource { 10 | padding-right: 3px; 11 | white-space: nowrap; 12 | 13 | &:before { 14 | white-space: normal; 15 | content: "/ "; 16 | color: $primaryLight; 17 | margin-right: 2px; 18 | } 19 | } 20 | } 21 | 22 | .document__metadata__resources { 23 | .resource-browser__resource { 24 | padding: 0; 25 | &:before { 26 | margin-right: 0; 27 | } 28 | } 29 | .resource-browser__path { 30 | padding: 3px 10px; 31 | } 32 | } -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_sparkchart.scss: -------------------------------------------------------------------------------- 1 | .sparkchart-bar { 2 | position: relative; 3 | width: 100%; 4 | height: 1rem; 5 | background-color: $secondary; 6 | display: flex; 7 | } 8 | 9 | .sparkchart-bar__cell { 10 | height: 1rem; 11 | } 12 | 13 | .sparkchart-bar__success { 14 | background-color: $accessibleGreen; 15 | } 16 | 17 | .sparkchart-bar__failure { 18 | // TODO: replace this with an accessible red that doesn't strobe 19 | background-color: $warningColour; 20 | } -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_textpopover.scss: -------------------------------------------------------------------------------- 1 | .textpopover { 2 | background-color: $primary; 3 | padding: 5px; 4 | margin: 5px; 5 | 6 | &__button { 7 | margin-right: 5px; 8 | } 9 | 10 | &-wrapper::after { 11 | position: relative; 12 | height: 0; 13 | width: 0; 14 | content: ""; 15 | bottom: 5px; 16 | display: block; 17 | left: calc(50% - 10px); 18 | border: { 19 | top: 10px solid $primary; 20 | left: 10px solid transparent; 21 | right: 10px solid transparent; 22 | } 23 | } 24 | 25 | &-icon { 26 | width: 20px; 27 | height: 20px; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_upload-dialog.scss: -------------------------------------------------------------------------------- 1 | .upload-dialog__file-browser { 2 | max-height: 400px; 3 | overflow-y: scroll; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/stylesheets/components/_upload-files.scss: -------------------------------------------------------------------------------- 1 | .file-upload__button { 2 | padding-bottom: 5px; 3 | margin-right: 12px; 4 | } 5 | 6 | .file-upload__icon { 7 | margin-right: 3px; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/stylesheets/eui-main.scss: -------------------------------------------------------------------------------- 1 | @charset 'UTF-8'; 2 | 3 | @import 4 | './abstracts/variables'; 5 | 6 | @import 7 | './components/document', 8 | './components/comments'; 9 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react-jsx" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /postgres/.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs 18.16.0 2 | -------------------------------------------------------------------------------- /postgres/cdk/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | !jest.config.js 3 | !jest.setup.js 4 | !.eslintrc.js 5 | *.d.ts 6 | node_modules 7 | dist 8 | 9 | # CDK asset staging directory 10 | .cdk.staging 11 | cdk.out 12 | -------------------------------------------------------------------------------- /postgres/cdk/.nvmrc: -------------------------------------------------------------------------------- 1 | 18.16.0 -------------------------------------------------------------------------------- /postgres/cdk/README.md: -------------------------------------------------------------------------------- 1 | # Infrastructure 2 | 3 | This directory defines the components to be deployed to AWS. 4 | 5 | See [`package.json`](package.json) for a list of available scripts. 6 | -------------------------------------------------------------------------------- /postgres/cdk/bin/cdk.ts: -------------------------------------------------------------------------------- 1 | import 'source-map-support/register'; 2 | import { App } from 'aws-cdk-lib'; 3 | import { Giant } from '../lib/giant'; 4 | 5 | const app = new App(); 6 | new Giant(app, 'pfi-giant-postgres-CODE', { 7 | stack: 'pfi-giant', 8 | stage: 'CODE', 9 | app: 'postgres', 10 | }); 11 | new Giant(app, 'pfi-giant-postgres-PROD', { 12 | stack: 'pfi-giant', 13 | stage: 'PROD', 14 | app: 'postgres', 15 | }); -------------------------------------------------------------------------------- /postgres/cdk/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node bin/cdk.ts", 3 | "context": { 4 | "aws-cdk:enableDiffNoFail": "true", 5 | "@aws-cdk/core:stackRelativeExports": "true" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /postgres/cdk/jest.setup.js: -------------------------------------------------------------------------------- 1 | jest.mock("@guardian/cdk/lib/constants/tracking-tag"); 2 | -------------------------------------------------------------------------------- /postgres/cdk/lib/__snapshots__/giant.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`The Giant stack matches the snapshot 1`] = ` 4 | { 5 | "Metadata": { 6 | "gu:cdk:constructs": [], 7 | "gu:cdk:version": "TEST", 8 | }, 9 | } 10 | `; 11 | -------------------------------------------------------------------------------- /postgres/cdk/lib/giant.test.ts: -------------------------------------------------------------------------------- 1 | import { App } from "aws-cdk-lib"; 2 | import { Template } from "aws-cdk-lib/assertions"; 3 | import { Giant } from "./giant"; 4 | 5 | describe("The Giant stack", () => { 6 | it("matches the snapshot", () => { 7 | const app = new App(); 8 | const stack = new Giant(app, "Giant", { stack: "pfi-playground", stage: "TEST" }); 9 | const template = Template.fromStack(stack); 10 | expect(template.toJSON()).toMatchSnapshot(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /postgres/migrate-db/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | node_modules/* -------------------------------------------------------------------------------- /postgres/migrate-db/.nvmrc: -------------------------------------------------------------------------------- 1 | 18.16.0 -------------------------------------------------------------------------------- /postgres/migrate-db/esbuild-runner.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: "bundle", 3 | esbuild: { 4 | external: ["pg", "pg-format"], 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /postgres/migrate-db/src/migrations/001.do.create_table_ingestion_events.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE ingestion_events ( 2 | ingest_id TEXT NOT NULL, 3 | blob_id TEXT NOT NULL, 4 | "type" TEXT NOT NULL, 5 | status TEXT NOT NULL, 6 | details JSONB, 7 | event_time TIMESTAMPTZ NOT NULL 8 | ); 9 | 10 | CREATE TABLE blob_metadata ( 11 | ingest_id TEXT NOT NULL, 12 | blob_id TEXT NOT NULL, 13 | path TEXT NOT NULL, -- includes file name 14 | file_size BIGINT NOT NULL, 15 | insert_time TIMESTAMPTZ NOT NULL 16 | ); 17 | CREATE INDEX ON ingestion_events (ingest_id, blob_id); 18 | CREATE INDEX ON blob_metadata (ingest_id, blob_id); 19 | CREATE INDEX ON ingestion_events ("type"); 20 | -- TODO think about indexing details->>'extractors' 21 | -- TODO think about indexing workspace -------------------------------------------------------------------------------- /postgres/migrate-db/src/migrations/001.undo.create_table_ingestion_events.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE ingestion_events; 2 | DROP TABLE blob_metadata; -------------------------------------------------------------------------------- /postgres/migrate-db/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "isolatedModules": true, 8 | "lib": ["esnext"], 9 | "module": "commonjs", 10 | "moduleResolution": "node", 11 | "noEmit": false, 12 | "noFallthroughCasesInSwitch": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true, 15 | "strict": true, 16 | "target": "esnext" 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.9.7 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | 2 | libraryDependencies += "org.vafer" % "jdeb" % "1.10" artifacts (Artifact("jdeb", "jar", "jar")) 3 | 4 | addSbtPlugin("org.playframework" % "sbt-plugin" % "3.0.5") 5 | 6 | addSbtPlugin("com.gu" % "sbt-riffraff-artifact" % "1.1.18") 7 | 8 | addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0") 9 | -------------------------------------------------------------------------------- /scripts/db/migrate-db-local.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | SCRIPT_PATH=$( cd $(dirname $0) ; pwd -P ) 5 | MIGRATE_DIRECTORY="$SCRIPT_PATH/../../postgres/migrate-db" 6 | 7 | echo "change directory to $DATA_DIRECTORY" 8 | 9 | cd $MIGRATE_DIRECTORY 10 | 11 | EXPECTED_NODE_VERSION=$(head -1 .nvmrc) 12 | CURRENT_NODE_VERSION=$(node --version) 13 | 14 | if [[ $CURRENT_NODE_VERSION = *"$EXPECTED_NODE_VERSION"* ]]; then 15 | npm install 16 | npm run start DEV 8432 17 | else 18 | echo -e "\033[0;31m ERROR current directory NODE version $CURRENT_NODE_VERSION does not match the expected version $EXPECTED_NODE_VERSION" 1>&2 19 | exit 1 20 | fi -------------------------------------------------------------------------------- /scripts/db/psql-local.sh: -------------------------------------------------------------------------------- 1 | #/usr/bin/env bash 2 | PGPASSWORD=giant psql -h localhost -p 8432 -U giant_master -d giant "$@" 3 | -------------------------------------------------------------------------------- /scripts/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | . ${0%/*}/setup_whisper.sh 4 | brew bundle 5 | pushd frontend 6 | npm install 7 | popd 8 | -------------------------------------------------------------------------------- /scripts/setup_whisper.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # install whisper in /opt/whisper/ in order to match path on EC2 3 | 4 | echo "Cloning whisper.cpp" 5 | sudo git clone git@github.com:ggerganov/whisper.cpp.git /opt/whisper/whisper.cpp 6 | 7 | echo "downloading ggml model" 8 | sudo bash /opt/whisper/whisper.cpp/models/download-ggml-model.sh base 9 | 10 | echo "compiling whisper.cpp" 11 | pushd /opt/whisper/whisper.cpp 12 | sudo make 13 | popd 14 | -------------------------------------------------------------------------------- /scripts/start-frontend.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Checking backend is running" 4 | 5 | curl \ 6 | --retry 60 \ 7 | --retry-delay 5 \ 8 | --retry-connrefused \ 9 | 'http://localhost:9001/healthcheck' 1>/dev/null 10 | 11 | if [ $? -eq 0 ]; then 12 | pushd frontend 13 | npm run start 14 | popd 15 | else 16 | echo "Backend is not running. Launch it using ./scripts/start-backend.sh" 17 | fi 18 | -------------------------------------------------------------------------------- /util/local-cerebro.conf: -------------------------------------------------------------------------------- 1 | include "./application.conf" 2 | 3 | hosts = [ 4 | { 5 | host = "http://elasticsearch:9200" 6 | name = "Local Elasticsearch" 7 | } 8 | ] -------------------------------------------------------------------------------- /util/nginx-mapping.yml: -------------------------------------------------------------------------------- 1 | name: pfi 2 | mappings: 3 | - prefix: pfi 4 | port: 3000 5 | - prefix: neo4j.pfi 6 | port: 7474 7 | - prefix: bolt.neo4j.pfi 8 | websocket: / 9 | port: 7687 10 | - prefix: elasticsearch.pfi 11 | port: 9200 12 | - prefix: minio.pfi 13 | port: 9090 14 | - prefix: cerebro.pfi 15 | port: 9091 16 | --------------------------------------------------------------------------------