mirror of
https://github.com/bellingcat/auto-archiver-setup-tool.git
synced 2026-06-11 21:18:38 +03:00
renaming, reordering, dropping firestore
This commit is contained in:
@@ -25,9 +25,9 @@
|
||||
size="large">
|
||||
open sheet
|
||||
</v-btn>
|
||||
<v-btn v-if="actionIsCreate" color="primary" size="large" :disabled="!requiredData"
|
||||
<v-btn v-if="actionIsCreate" color="teal" size="large" :disabled="!requiredData"
|
||||
@click="createSheet">Create</v-btn>
|
||||
<v-btn v-if="!actionIsCreate" color="primary" size="large" :disabled="!requiredDataExisting"
|
||||
<v-btn v-if="!actionIsCreate" color="teal" size="large" :disabled="!requiredDataExisting"
|
||||
@click="addExistingSheet">Add Existing Sheet</v-btn>
|
||||
</v-col>
|
||||
|
||||
@@ -58,9 +58,6 @@ export default {
|
||||
snackbarColor: "red",
|
||||
loading: false,
|
||||
|
||||
tab: '',
|
||||
items: ['Create new Archiver Sheet', 'Add existing Sheets'],
|
||||
|
||||
sheetName: ``.trim(),
|
||||
sheetUrlId: ``,
|
||||
|
||||
@@ -109,8 +106,7 @@ export default {
|
||||
|
||||
this.loading = true;
|
||||
this.newSheetId = "";
|
||||
this.$store.dispatch("add", this.sheetName).then((res) => {
|
||||
console.log(res);
|
||||
this.$store.dispatch("createSheet", this.sheetName).then((res) => {
|
||||
if (!res.success) throw new Error(res.result);
|
||||
this.newSheetId = res.result;
|
||||
this.addSheetToAPI(this.newSheetId);
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
<template>
|
||||
<v-container class="pane">
|
||||
<v-card class="pa-3">
|
||||
<v-card class="pa-0">
|
||||
|
||||
<v-card-title class="text-center">
|
||||
Archive Google Spreadsheets
|
||||
</v-card-title>
|
||||
|
||||
<v-tabs v-model="tab" bg-color="green-lighten-4" grow class="elevation-1 rounded">
|
||||
<v-tabs v-model="tab" bg-color="teal" grow class="elevation-1">
|
||||
<v-tab v-for="item in items" :key="item" :text="item" :value="item"></v-tab>
|
||||
</v-tabs>
|
||||
|
||||
@@ -97,45 +93,8 @@
|
||||
</v-tabs-window-item>
|
||||
</v-tabs-window>
|
||||
</v-card>
|
||||
|
||||
</v-container>
|
||||
|
||||
<v-container class="pane-l">
|
||||
<v-card>
|
||||
<v-card-title class="text-center">
|
||||
<h4>Your Sheets</h4>
|
||||
</v-card-title>
|
||||
|
||||
<v-table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left">Sheet Name</th>
|
||||
<th class="text-left">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Sheet 1</td>
|
||||
<td>
|
||||
<v-btn color="primary" @click="archiveSheet('Sheet 1')">Archive</v-btn>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sheet 2</td>
|
||||
<td>
|
||||
<v-btn color="primary" @click="archiveSheet('Sheet 2')">Archive</v-btn>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sheet 3</td>
|
||||
<td>
|
||||
<v-btn color="primary" @click="archiveSheet('Sheet 3')">Archive</v-btn>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -149,7 +108,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
tab: '',
|
||||
items: ['Create new Archiver Sheet', 'Add existing Sheets'],
|
||||
items: ['Create new Archiver Sheet', 'Add existing Sheet'],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-container class="pane">
|
||||
<v-container class="pane" v-if="user?.active" >
|
||||
<v-card :loading="loadingArchive">
|
||||
<v-card-title class="text-center">
|
||||
Archive a single URL
|
||||
@@ -20,7 +20,7 @@
|
||||
density="compact"></v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4" class="text-right">
|
||||
<v-btn @click="archiveUrl" color="primary"
|
||||
<v-btn @click="archiveUrl" color="teal"
|
||||
:disabled="!validUrl || loadingArchive || (!public && group == -1)">
|
||||
Archive
|
||||
</v-btn>
|
||||
@@ -29,7 +29,7 @@
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<p v-if="loadingArchive">
|
||||
<v-progress-circular color="primary" indeterminate></v-progress-circular>
|
||||
<v-progress-circular color="teal" indeterminate></v-progress-circular>
|
||||
Archive in progress task id = <code>{{ taskId }}</code>
|
||||
</p>
|
||||
<v-alert color="success" icon="mdi-information" v-if="archiveResult">
|
||||
@@ -41,7 +41,7 @@
|
||||
</v-alert>
|
||||
<p v-if="validUrl">
|
||||
You can <strong v-if="archiveFailure">still</strong> <router-link
|
||||
:to="`/urls?url=${encodeURIComponent(url)}`" target="_blank"><v-icon>mdi-open-in-new</v-icon> search for
|
||||
:to="`/archives?url=${encodeURIComponent(url)}`" target="_blank"><v-icon>mdi-open-in-new</v-icon> search for
|
||||
archives</router-link> of
|
||||
this URL.
|
||||
</p>
|
||||
@@ -81,6 +81,9 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.state.user;
|
||||
},
|
||||
urlValidator() {
|
||||
return urlValidator;
|
||||
},
|
||||
@@ -92,9 +95,6 @@ export default {
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// public(val) {
|
||||
// if (!val) this.loadGroups();
|
||||
// },
|
||||
url(val) {
|
||||
this.archiveResult = null;
|
||||
this.archiveFailure = null;
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<v-card class="doc">
|
||||
<v-card-title>{{ doc.name }}</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="doc-timestamp">
|
||||
Created: {{ new Date(doc.timestamp).toLocaleString() }}
|
||||
</div>
|
||||
<div class="doc-archived">
|
||||
Last archived:
|
||||
{{
|
||||
doc.lastArchived
|
||||
? new Date(doc.lastArchived).toLocaleString()
|
||||
: "never"
|
||||
}}
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-btn :href="doc.url" target="_blank"
|
||||
>Open sheet
|
||||
<v-icon size="small" style="margin-left: 1em"
|
||||
>mdi-open-in-new</v-icon
|
||||
>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-btn @click="$store.dispatch('archive', doc)">Archive now</v-btn>
|
||||
</v-col>
|
||||
<v-dialog width="500" v-model="dialog" persistent :retain-focus="false">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-col class="text-right">
|
||||
<v-btn color="#f2d97c" location="right" v-bind="props"
|
||||
>Stop archiving</v-btn
|
||||
>
|
||||
</v-col>
|
||||
</template>
|
||||
|
||||
<v-card>
|
||||
<v-card-title>Stop archiving "{{ doc.name }}"? </v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
This will stop archiving the sheet, but will not delete the sheet
|
||||
or any of its data from your Google Drive.
|
||||
</v-card-text>
|
||||
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-actions>
|
||||
<v-btn @click="dialog = false" color="primary">Cancel</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="red" variant="text" @click="remove">
|
||||
Stop archiving
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-row>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DocCard",
|
||||
props: {
|
||||
doc: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
remove() {
|
||||
this.dialog = false;
|
||||
this.$store.dispatch("removeDoc", this.doc.id);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,30 +0,0 @@
|
||||
<template>
|
||||
<div style="margin-bottom: 1em">
|
||||
<div class="text-h5 mt-5 mb-3" v-if="docs.length > 0">
|
||||
Your auto-archiver sheets
|
||||
</div>
|
||||
<v-row v-for="doc in docs" :key="doc.sheetId">
|
||||
<v-col>
|
||||
<DocCard :doc="doc" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DocCard from "@/components/DocCard.vue";
|
||||
|
||||
export default {
|
||||
name: "DocList",
|
||||
components: {
|
||||
DocCard,
|
||||
},
|
||||
computed: {
|
||||
docs() {
|
||||
return this.$store.state.docs;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
129
src/components/ManageSheets.vue
Normal file
129
src/components/ManageSheets.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
|
||||
<v-container class="pane-l mb-10">
|
||||
<v-card class="pa-3">
|
||||
|
||||
<v-card-title class="text-center">
|
||||
Your <span v-if="items">{{ items.length }}</span> Sheets
|
||||
</v-card-title>
|
||||
|
||||
<v-data-table :headers="headers" item-key="name" no-data-text="No Active Sheets available" :items="items"
|
||||
:loading="loading" items-per-page="25" hover>
|
||||
<template v-slot:item.actions="{ item: data }">
|
||||
<v-btn color="green" size="small" icon class="mx-2" rounded
|
||||
:href="`https://docs.google.com/spreadsheets/d/${data.id}`" :disabled="loading"
|
||||
target="_blank"><v-icon>mdi-open-in-new</v-icon>
|
||||
<v-tooltip activator="parent" location="left">Open in new tab</v-tooltip>
|
||||
</v-btn>
|
||||
<v-btn color="" size="small" icon class="mx-2" :disabled="loading" rounded
|
||||
@click="archiveSheetNow(data.id)"><v-icon>mdi-archive-outline</v-icon>
|
||||
|
||||
<v-tooltip activator="parent" location="left">Archive Now!</v-tooltip>
|
||||
</v-btn>
|
||||
<v-btn color="red-lighten-4" size="small" icon class="mx-2" :disabled="loading" rounded
|
||||
@click="removeSheet(data.id)"><v-icon>mdi-delete-outline</v-icon>
|
||||
<v-tooltip activator="parent" location="left">Stop archiving, does not delete the spreadsheet
|
||||
itself.</v-tooltip>
|
||||
</v-btn>
|
||||
</template>
|
||||
<template v-slot:item.name="{ item: data }">
|
||||
<span :title="data.id">{{ data.name }}</span>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-card>
|
||||
</v-container>
|
||||
|
||||
<SnackBar :message="snackbarMessage" :show="snackbar" :color="snackbarColor" @update:show="snackbar = $event" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SnackBar from "@/components/SnackBar.vue";
|
||||
|
||||
export default {
|
||||
name: "ManageSheets",
|
||||
components: {
|
||||
SnackBar,
|
||||
},
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
snackbar: false,
|
||||
snackbarMessage: "",
|
||||
snackbarColor: "red",
|
||||
|
||||
loading: false,
|
||||
|
||||
headers: [
|
||||
{ title: "Sheet Name", value: "name", sortable: true },
|
||||
{ title: "Group", value: "group_id", sortable: true },
|
||||
{ title: "Frequency", value: "frequency", sortable: true },
|
||||
{ title: "Created", value: "created_at", sortable: true },
|
||||
{ title: "Last Archived", value: "last_archived_at", sortable: true },
|
||||
{ title: 'Actions', value: "actions", align: 'center' },
|
||||
],
|
||||
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.state.user;
|
||||
},
|
||||
items() {
|
||||
return this.$store.state.sheets;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showSnackbar(message, color = "red") {
|
||||
this.snackbarMessage = message;
|
||||
this.snackbarColor = color;
|
||||
this.snackbar = true;
|
||||
},
|
||||
archiveSheetNow(sheetId) {
|
||||
this.loading = true;
|
||||
fetch(`${this.$store.state.API_ENDPOINT}/sheet/${sheetId}/archive`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${this.$store.state.access_token}`,
|
||||
}
|
||||
}).then(async response => {
|
||||
const res = await response.json();
|
||||
if (response.status === 201) {
|
||||
this.showSnackbar(`Sheet ${sheetId} is being archived with task id ${res?.id}!`, "green");
|
||||
this.$store.dispatch("getSheets");
|
||||
} else {
|
||||
throw new Error(JSON.stringify(res));
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error("/sheet/mine ", error);
|
||||
this.showSnackbar(`Unable to trigger sheet archive: ${error.message}`);
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
removeSheet(sheetId) {
|
||||
this.loading = true;
|
||||
fetch(`${this.$store.state.API_ENDPOINT}/sheet/${sheetId}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${this.$store.state.access_token}`,
|
||||
}
|
||||
}).then(async response => {
|
||||
const res = await response.json();
|
||||
if (response.status === 200 && res.deleted) {
|
||||
this.showSnackbar(`Sheet ${sheetId} has been removed!`, "green");
|
||||
this.$store.dispatch("getSheets");
|
||||
} else {
|
||||
throw new Error(JSON.stringify(res));
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error("/sheet/mine ", error);
|
||||
this.showSnackbar(`Unable to remove sheet: ${error.message}`);
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<v-app-bar style="flex-grow: 0" class="text-no-wrap">
|
||||
<v-toolbar-title title="Bellingcat Auto Archiver demo">
|
||||
<v-toolbar-title title="Bellingcat Auto Archiver prototype">
|
||||
<router-link to="/" class="nodecoration">
|
||||
Bellingcat Auto Archiver demo
|
||||
Bellingcat Auto Archiver prototype
|
||||
</router-link>
|
||||
</v-toolbar-title>
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
</v-chip>
|
||||
|
||||
<v-spacer v-if="!smAndDown"></v-spacer>
|
||||
<div v-if="user && !smAndDown">
|
||||
<div v-if="user?.active && !smAndDown">
|
||||
<template v-for="btn in btns">
|
||||
<v-btn :to="btn.to" :prepend-icon="btn.icon" variant="text" class="nodecoration" size="large">
|
||||
<v-btn :to="btn.to" :prepend-icon="btn.icon" variant="text" class="nodecoration ml-2" size="large" active-color="teal">
|
||||
{{ btn.text }}
|
||||
<v-tooltip activator="parent" location="bottom">{{ btn.tooltip }}</v-tooltip>
|
||||
</v-btn>
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
<v-btn v-if="!user" @click="$store.dispatch('signin')">Sign In</v-btn>
|
||||
|
||||
<v-menu v-if="user && smAndDown">
|
||||
<v-menu v-if="user?.active && smAndDown">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-app-bar-nav-icon v-bind="props"></v-app-bar-nav-icon>
|
||||
</template>
|
||||
@@ -73,9 +73,9 @@ export default {
|
||||
return {
|
||||
drawer: false,
|
||||
btns: [
|
||||
{ to: "/", icon: "mdi-cloud-download-outline", text: "URL", tooltip: "Archive a single URL." },
|
||||
{ to: "/urls", icon: "mdi-magnify", text: "Archives", tooltip: "Search for archived URLs." },
|
||||
{ to: "/sheets", icon: "mdi-table-large", text: "Sheets", tooltip: "Create, manage, and archive Google Sheets." }
|
||||
{ to: "/", icon: "mdi-table-large", text: "Sheets", tooltip: "Create, manage, and archive Google Sheets." },
|
||||
{ to: "/url", icon: "mdi-cloud-download-outline", text: "URL", tooltip: "Archive a single URL." },
|
||||
{ to: "/archives", icon: "mdi-magnify", text: "Archives", tooltip: "Search for archived URLs." },
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import HomeView from '../views/HomeView.vue';
|
||||
import SheetView from '../views/SheetView.vue';
|
||||
import UrlView from '../views/UrlView.vue';
|
||||
import ArchivesView from '../views/ArchivesView.vue';
|
||||
|
||||
import ArchiveUrl from "../components/ArchiveUrl.vue";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -10,14 +11,14 @@ const routes = [
|
||||
component: HomeView,
|
||||
},
|
||||
{
|
||||
path: '/sheets',
|
||||
name: 'Google Sheets Archiving',
|
||||
component: SheetView,
|
||||
path: '/url',
|
||||
name: 'URL Archiving',
|
||||
component: ArchiveUrl,
|
||||
},
|
||||
{
|
||||
path: '/urls',
|
||||
name: 'URL Archiving',
|
||||
component: UrlView,
|
||||
path: '/archives',
|
||||
name: 'Archives search',
|
||||
component: ArchivesView,
|
||||
},
|
||||
{
|
||||
path: '/privacy',
|
||||
|
||||
@@ -5,17 +5,7 @@ import {
|
||||
GoogleAuthProvider,
|
||||
signInWithCredential,
|
||||
} from "firebase/auth";
|
||||
import {
|
||||
collection,
|
||||
addDoc,
|
||||
query,
|
||||
where,
|
||||
limit,
|
||||
getDocs,
|
||||
doc,
|
||||
deleteDoc,
|
||||
updateDoc,
|
||||
} from "firebase/firestore";
|
||||
import { collection, } from "firebase/firestore";
|
||||
import { firebaseAuth, firebaseFirestore } from "@/firebase.js";
|
||||
|
||||
function saveToLocalStorage(state) {
|
||||
@@ -33,6 +23,7 @@ function clearLocalStorage() {
|
||||
localStorage.removeItem("user");
|
||||
localStorage.removeItem("access_token");
|
||||
}
|
||||
|
||||
async function waitForGapiAuth2() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const checkGapiAuth2 = () => {
|
||||
@@ -51,7 +42,7 @@ export default createStore({
|
||||
user: null,
|
||||
active: false,
|
||||
access_token: null,
|
||||
docs: [],
|
||||
sheets: [],
|
||||
loading: false,
|
||||
errorMessage: "",
|
||||
// API_ENDPOINT: "https://auto-archiver-api.bellingcat.com"
|
||||
@@ -69,8 +60,8 @@ export default createStore({
|
||||
state.user.groups = groups;
|
||||
saveToLocalStorage(state);
|
||||
},
|
||||
setDocs(state, docs) {
|
||||
state.docs = docs;
|
||||
setSheets(state, sheets) {
|
||||
state.sheets = sheets;
|
||||
},
|
||||
setLoading(state, loading) {
|
||||
state.loading = loading;
|
||||
@@ -95,7 +86,7 @@ export default createStore({
|
||||
commit("setUser", response.user);
|
||||
dispatch("checkActiveUser");
|
||||
dispatch("checkUserGroups");
|
||||
dispatch("getDocs");
|
||||
dispatch("getSheets");
|
||||
}
|
||||
|
||||
commit("setUser", null);
|
||||
@@ -126,7 +117,7 @@ export default createStore({
|
||||
|
||||
// clean user from store and local storage
|
||||
commit("setUser", null);
|
||||
commit("setDocs", []);
|
||||
commit("setSheets", []);
|
||||
clearLocalStorage();
|
||||
} catch (error) {
|
||||
console.error("signOutUser (firebase/auth.js): ", error);
|
||||
@@ -175,71 +166,35 @@ export default createStore({
|
||||
}
|
||||
},
|
||||
|
||||
async getDocs({ state, commit }) {
|
||||
if (!state.user || !state.user.active) {
|
||||
return;
|
||||
}
|
||||
async getSheets({ state, commit }) {
|
||||
try {
|
||||
// get documents where uid matches user
|
||||
commit("setLoading", true);
|
||||
commit("setErrorMessage", "");
|
||||
if(state.user?.active === false) return;
|
||||
|
||||
const q = query(
|
||||
collection(firebaseFirestore, "sheets"),
|
||||
where("uid", "==", state.user.uid)
|
||||
);
|
||||
|
||||
const response = await getDocs(q);
|
||||
|
||||
const docs = response.docs.map((d) => ({ id: d.id, ...d.data() }));
|
||||
commit("setDocs", docs);
|
||||
commit("setLoading", false);
|
||||
} catch (error) {
|
||||
console.error("getDocs (firebase.js): ", error);
|
||||
}
|
||||
},
|
||||
|
||||
async removeDoc({ dispatch }, id) {
|
||||
try {
|
||||
await deleteDoc(doc(firebaseFirestore, "sheets", id));
|
||||
|
||||
dispatch("getDocs");
|
||||
} catch (error) {
|
||||
console.error("removeDocs (firebase.js): ", error);
|
||||
}
|
||||
},
|
||||
|
||||
async archive({ state, dispatch }, sheet) {
|
||||
try {
|
||||
// send a post request to the API with the sheet ID in the body
|
||||
// and a bearer auth token in the header
|
||||
await fetch(
|
||||
`${state.API_ENDPOINT}/sheet`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${state.access_token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
sheet_id: sheet.sheetId,
|
||||
}),
|
||||
fetch(`${state.API_ENDPOINT}/sheet/mine`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${state.access_token}`,
|
||||
}
|
||||
);
|
||||
|
||||
// update firestore with the archive status
|
||||
const docRef = doc(firebaseFirestore, "sheets", sheet.id);
|
||||
|
||||
await updateDoc(docRef, {
|
||||
lastArchived: Date.now(),
|
||||
}).then(async response => {
|
||||
const res = await response.json();
|
||||
if (response.status === 200) {
|
||||
commit("setSheets", res);
|
||||
} else {
|
||||
throw new Error(JSON.stringify(res));
|
||||
}
|
||||
}).finally(() => {
|
||||
commit("setLoading", false);
|
||||
});
|
||||
|
||||
// update the store
|
||||
dispatch("getDocs");
|
||||
} catch (error) {
|
||||
console.error("archive (firebase.js): ", error);
|
||||
console.error("getSheets (firebase.js): ", error);
|
||||
}
|
||||
},
|
||||
|
||||
async add({ state, dispatch, commit }, name) {
|
||||
},
|
||||
async createSheet({ state, dispatch, commit }, name) {
|
||||
commit("setLoading", true);
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
@@ -391,49 +346,6 @@ export default createStore({
|
||||
commit("setLoading", false);
|
||||
});
|
||||
},
|
||||
|
||||
async enable({ state, dispatch, commit }, { spreadsheetId }) {
|
||||
commit("setLoading", true);
|
||||
commit("setErrorMessage", "");
|
||||
|
||||
try {
|
||||
// fetch existing sheet
|
||||
console.log(spreadsheetId);
|
||||
const sheetToEnable = await gapi.client.sheets.spreadsheets.get({
|
||||
spreadsheetId: spreadsheetId,
|
||||
});
|
||||
console.log(sheetToEnable);
|
||||
|
||||
const q = query(
|
||||
collection(firebaseFirestore, "sheets"),
|
||||
where("uid", "==", state.user.uid),
|
||||
where("sheetId", "==", spreadsheetId),
|
||||
limit(1)
|
||||
);
|
||||
|
||||
const response = await getDocs(q);
|
||||
if (response.docs.length > 0) {
|
||||
throw "Sheet already enabled";
|
||||
}
|
||||
|
||||
const col = await collection(firebaseFirestore, "sheets");
|
||||
await addDoc(col, {
|
||||
sheetId: spreadsheetId,
|
||||
url: sheetToEnable.result.spreadsheetUrl,
|
||||
timestamp: Date.now(),
|
||||
uid: state.user.uid,
|
||||
email: state.user.email,
|
||||
lastArchived: null,
|
||||
name: sheetToEnable.result.properties.title,
|
||||
});
|
||||
|
||||
dispatch("getDocs");
|
||||
} catch (error) {
|
||||
commit("setErrorMessage", `Unable to add sheet: ${JSON.stringify(error)}`);
|
||||
commit("setLoading", false);
|
||||
console.error("add (firebase.js): ", error);
|
||||
}
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
isTokenExpired: async (state) => {
|
||||
@@ -469,7 +381,7 @@ export default createStore({
|
||||
//TODO: merge these into single endpoint in the future
|
||||
store.dispatch("checkActiveUser");
|
||||
store.dispatch("checkUserGroups");
|
||||
store.dispatch("getDocs");
|
||||
store.dispatch("getSheets");
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error("Error checking token expiration:", error);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-container class="pane-l">
|
||||
<v-container class="pane-l" v-if="user?.active">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-card elevation="12">
|
||||
@@ -23,7 +23,7 @@
|
||||
variant="outlined" :rules="[urlValidator]" required @keyup.enter="searchForArchives"></v-text-field>
|
||||
<v-row>
|
||||
<v-col cols="12" class="text-right">
|
||||
<v-btn @click="searchForArchives" color="primary" class="mt-4" size="large" :disabled="!validUrl">
|
||||
<v-btn @click="searchForArchives" color="teal" class="mt-4" size="large" :disabled="!validUrl">
|
||||
Search
|
||||
</v-btn>
|
||||
</v-col>
|
||||
@@ -103,7 +103,7 @@
|
||||
<script>
|
||||
import { urlValidator, getUrlFromResult } from "@/utils/misc.js";
|
||||
export default {
|
||||
name: "UrlView",
|
||||
name: "ArchivesView",
|
||||
data() {
|
||||
return {
|
||||
today: new Date().toISOString().substring(0, 10),
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-container class="pane" fluid>
|
||||
<v-container class="pane" fluid v-if="!user || !user.active" >
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-alert color="orange" icon="mdi-information" v-if="user && !user.active">
|
||||
@@ -17,7 +17,7 @@
|
||||
data.
|
||||
</v-alert>
|
||||
<p>
|
||||
This tool can be used to archive digital content via single URL or Google Sheets, you can also <router-link to="/urls">search for archive content</router-link>.
|
||||
This tool can be used to archive digital content via single URL or Google Sheets, you can also search for archived content.
|
||||
</p>
|
||||
<p v-if="!user || !user.active">
|
||||
To use this tool you need a Google account and <strong>permission from the Bellingcat team</strong>.
|
||||
@@ -27,16 +27,18 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<ArchiveUrl v-if="user?.active" />
|
||||
<ArchiveSheet v-if="user?.active" />
|
||||
<ManageSheets v-if="user?.active" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ArchiveUrl from "@/components/ArchiveUrl.vue";
|
||||
import ArchiveSheet from "@/components/ArchiveSheet.vue";
|
||||
import ManageSheets from "@/components/ManageSheets.vue";
|
||||
|
||||
export default {
|
||||
name: "HomeView",
|
||||
components: {
|
||||
ArchiveUrl
|
||||
ArchiveSheet, ManageSheets
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
|
||||
@@ -1,193 +1,26 @@
|
||||
<template>
|
||||
<ArchiveSheet v-if="user?.active" />
|
||||
<v-container class="pane">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-card style="margin-bottom: 1em">
|
||||
<v-card-text>
|
||||
<v-alert color="#f2d97c" icon="mdi-alert">
|
||||
This is still a pre-release prototype demo service provided on a
|
||||
best-effort basis. Do not use for mission critical or sensitive
|
||||
data.
|
||||
</v-alert>
|
||||
<p>
|
||||
This tool will configure a Google Sheet on your Google account for
|
||||
use with
|
||||
<a href="https://github.com/bellingcat/auto-archiver"
|
||||
>Bellingcat's Auto Archiver</a
|
||||
>. For more information about the Auto Archiver and how to use it,
|
||||
see
|
||||
<a href="https://github.com/bellingcat/auto-archiver"
|
||||
>our Github repository</a
|
||||
>
|
||||
and the
|
||||
<a
|
||||
href="https://www.bellingcat.com/resources/2022/09/22/preserve-vital-online-content-with-bellingcats-auto-archiver-tool/"
|
||||
>associated article</a
|
||||
>.
|
||||
</p>
|
||||
<h4>How archiving a Google Spreadsheet works</h4>
|
||||
<ul>
|
||||
<li>Add links to the <code>Link</code> column</li>
|
||||
<li>
|
||||
Links are archived
|
||||
<b>every 60 minutes</b>, or you can trigger a manual archive
|
||||
below
|
||||
</li>
|
||||
<li>
|
||||
You can modify and share the Google Sheet subsequently, but do
|
||||
not edit the auto archiver column names in the header row or
|
||||
remove the service account from the shared users
|
||||
</li>
|
||||
</ul>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<DocList v-if="user" />
|
||||
|
||||
<div class="text-h5 mt-5 mb-3">Manage new auto-archiver sheets</div>
|
||||
|
||||
<v-card style="margin-bottom: 1em">
|
||||
<v-card-title>Create a new auto-archiver sheet</v-card-title>
|
||||
<v-card-text>
|
||||
<ol style="margin-bottom: 1em">
|
||||
<li>Press "create" to create a new archiving Google Sheet</li>
|
||||
<li>
|
||||
This sheet will be shared with the service account necessary for
|
||||
Bellingcat's archiving server
|
||||
</li>
|
||||
<li>The sheet will appear in your list</li>
|
||||
</ol>
|
||||
<v-text-field
|
||||
label="Document name"
|
||||
v-model="docName"
|
||||
v-if="user"
|
||||
></v-text-field>
|
||||
<v-btn
|
||||
@click="$store.dispatch('add', { name: docName })"
|
||||
:loading="$store.state.loading"
|
||||
v-if="user"
|
||||
>Create</v-btn
|
||||
>
|
||||
<v-alert v-if="!user" color="#f2d97c" icon="mdi-alert"
|
||||
><a href="#!" @click="$store.dispatch('signin')"
|
||||
>Sign in with a Google account</a
|
||||
>
|
||||
to continue</v-alert
|
||||
>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-card>
|
||||
<v-card-title
|
||||
>Enable the auto-archiver in an existing sheet</v-card-title
|
||||
>
|
||||
<v-card-text>
|
||||
<ol style="margin-bottom: 1em">
|
||||
<li>
|
||||
Invite
|
||||
<code
|
||||
>bellingcat-auto-archiver-api@bellingcat-auto-archiver-b85db.iam.gserviceaccount.com</code
|
||||
>
|
||||
into your spreadsheet
|
||||
</li>
|
||||
<!-- Link Archive status Destination folder Archive location Archive date Thumbnail Upload timestamp Upload title Textual content Screenshot Hash -->
|
||||
<li>
|
||||
Make sure you have the following <b>mandatory</b> column names:
|
||||
<ul>
|
||||
<li><code>Link</code> where you will put the URLs.</li>
|
||||
<li>
|
||||
<code>Archive Status</code> to monitor progress and success
|
||||
of archiver
|
||||
</li>
|
||||
<li>
|
||||
<code>Archive location</code> where the link to the archived
|
||||
content is added
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
Add any of the following <b>optional</b> column names:
|
||||
<ul>
|
||||
<li>
|
||||
<code>Archive date</code> info on when archiving occurred
|
||||
</li>
|
||||
<li>
|
||||
<code>Thumbnail</code> an image preview from archived media
|
||||
</li>
|
||||
<li>
|
||||
<code>Upload timestamp</code> online content creation date
|
||||
</li>
|
||||
<li><code>Upload title</code> title</li>
|
||||
<li><code>Textual content</code> text content</li>
|
||||
<li><code>Screenshot</code> link to page screenshot</li>
|
||||
<li>
|
||||
<code>Hash</code> content hash (for integrity purposes)
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Paste the Google Sheet URL</li>
|
||||
<li>Press "enable" to add the Google Sheet to your list</li>
|
||||
<li>
|
||||
Manually check archiving is working and re-check the steps above
|
||||
if it is not
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<v-text-field
|
||||
label="Google Sheet URL"
|
||||
v-model="spreadsheetUrl"
|
||||
:hint="spreadsheetId ? 'Detected id: ' + spreadsheetId : ''"
|
||||
persistent-hint
|
||||
v-if="user"
|
||||
></v-text-field>
|
||||
<v-btn
|
||||
@click="$store.dispatch('enable', { spreadsheetId })"
|
||||
:loading="$store.state.loading"
|
||||
v-if="user"
|
||||
>Enable</v-btn
|
||||
>
|
||||
<v-alert v-if="!user" color="#f2d97c" light icon="mdi-alert"
|
||||
><a href="#!" @click="$store.dispatch('signin')"
|
||||
>Sign in with a Google account</a
|
||||
>
|
||||
to continue</v-alert
|
||||
>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<ManageSheets v-if="user?.active" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DocList from "@/components/DocList.vue";
|
||||
import ArchiveSheet from "@/components/ArchiveSheet.vue";
|
||||
import ManageSheets from "@/components/ManageSheets.vue";
|
||||
|
||||
export default {
|
||||
name: "SheetView",
|
||||
components: {
|
||||
DocList, ArchiveSheet
|
||||
ArchiveSheet, ManageSheets
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
docName: "Auto archiver sheet",
|
||||
spreadsheetUrl: "",
|
||||
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.state.user;
|
||||
},
|
||||
spreadsheetId() {
|
||||
if (
|
||||
this.spreadsheetUrl.startsWith("http") &&
|
||||
this.spreadsheetUrl.split("/").length >= 6
|
||||
) {
|
||||
return this.spreadsheetUrl.split("/")[5];
|
||||
}
|
||||
return this.spreadsheetUrl;
|
||||
},
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user