mirror of
https://github.com/bellingcat/auto-archiver-setup-tool.git
synced 2026-06-12 21:48:37 +03:00
create/add sheet logic
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "firebase-archiver-2",
|
"name": "firebase-archiver-2",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve --port 8081 --skip-plugins @vue/cli-plugin-eslint",
|
"serve": "vue-cli-service serve --port 8081 --skip-plugins @vue/cli-plugin-eslint",
|
||||||
|
|||||||
162
src/components/AddSheet.vue
Normal file
162
src/components/AddSheet.vue
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
<template>
|
||||||
|
<v-row class="my-2">
|
||||||
|
<v-col cols="12" sm="12" class="ma-0 pb-0">
|
||||||
|
<v-text-field label="Google Sheets document name" v-model="sheetName" required
|
||||||
|
density="comfortable"></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col v-if="!actionIsCreate" cols="12" sm="12" class="ma-0 py-0">
|
||||||
|
<v-text-field label="Existing Google Sheet URL/ID" v-model="sheetUrlId" required density="comfortable">
|
||||||
|
</v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6" sm="6" class="ma-0 py-0">
|
||||||
|
<v-select v-model="group" label="Group" :items="availableGroups" required density="comfortable"></v-select>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="6" sm="6" class="ma-0 py-0">
|
||||||
|
<v-select v-model="frequency" label="Archive frequency" :items="availableFrequencies" required
|
||||||
|
density="comfortable"></v-select>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" class="text-right pt-0">
|
||||||
|
<small v-if="spreadsheetId">Detected Spreadsheet id: <code>{{ spreadsheetId }}</code></small>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="12" class="text-right pt-0">
|
||||||
|
<v-progress-circular color="green" indeterminate class="mx-6" v-if="loading"></v-progress-circular>
|
||||||
|
<v-btn v-if="newSheetId" :href="`https://docs.google.com/spreadsheets/d/${newSheetId}`"
|
||||||
|
append-icon="mdi-open-in-new" :title="newSheetId" target="_blank" color="success" class="mx-2"
|
||||||
|
size="large">
|
||||||
|
open sheet
|
||||||
|
</v-btn>
|
||||||
|
<v-btn v-if="actionIsCreate" color="primary" size="large" :disabled="!requiredData"
|
||||||
|
@click="createSheet">Create</v-btn>
|
||||||
|
<v-btn v-if="!actionIsCreate" color="primary" size="large" :disabled="!requiredDataExisting"
|
||||||
|
@click="addExistingSheet">Add Existing Sheet</v-btn>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<SnackBar :message="snackbarMessage" :show="snackbar" :color="snackbarColor" @update:show="snackbar = $event" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SnackBar from "@/components/SnackBar.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "AddSheet",
|
||||||
|
components: {
|
||||||
|
SnackBar,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
actionIsCreate: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
snackbar: false,
|
||||||
|
snackbarMessage: "",
|
||||||
|
snackbarColor: "red",
|
||||||
|
loading: false,
|
||||||
|
|
||||||
|
tab: '',
|
||||||
|
items: ['Create new Archiver Sheet', 'Add existing Sheets'],
|
||||||
|
|
||||||
|
sheetName: ``.trim(),
|
||||||
|
sheetUrlId: ``,
|
||||||
|
|
||||||
|
group: "please select",
|
||||||
|
|
||||||
|
availableFrequencies: ["daily", "hourly"].map(f => ({ title: f, value: f })),
|
||||||
|
frequency: "daily",
|
||||||
|
|
||||||
|
newSheetId: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
user() {
|
||||||
|
return this.$store.state.user;
|
||||||
|
},
|
||||||
|
requiredData() {
|
||||||
|
return this.sheetName && this.availableGroups?.some(g => g.value === this.group) && this.availableFrequencies?.some(f => f.value === this.frequency);
|
||||||
|
},
|
||||||
|
requiredDataExisting() {
|
||||||
|
return this.sheetName && this.spreadsheetId && this.availableGroups?.some(g => g.value === this.group) && this.availableFrequencies?.some(f => f.value === this.frequency);
|
||||||
|
},
|
||||||
|
availableGroups() {
|
||||||
|
return (this.$store.state.user?.groups || []).map(g => ({ title: g, value: g }));
|
||||||
|
},
|
||||||
|
spreadsheetId() {
|
||||||
|
if (
|
||||||
|
this.sheetUrlId.startsWith("http") &&
|
||||||
|
this.sheetUrlId.split("/").length >= 6
|
||||||
|
) {
|
||||||
|
return this.sheetUrlId.split("/")[5];
|
||||||
|
}
|
||||||
|
return this.sheetUrlId;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showSnackbar(message, color = "red") {
|
||||||
|
this.snackbarMessage = message;
|
||||||
|
this.snackbarColor = color;
|
||||||
|
this.snackbar = true;
|
||||||
|
},
|
||||||
|
createSheet() {
|
||||||
|
if (!this.requiredData) return;
|
||||||
|
if (this.loading) return;
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
this.newSheetId = "";
|
||||||
|
this.$store.dispatch("add", this.sheetName).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
if (!res.success) throw new Error(res.result);
|
||||||
|
this.newSheetId = res.result;
|
||||||
|
this.addSheetToAPI(this.newSheetId);
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
this.showSnackbar(`Unable to create sheet: ${error.message}`);
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addExistingSheet() {
|
||||||
|
if (!this.requiredDataExisting) return;
|
||||||
|
if (this.loading) return;
|
||||||
|
this.loading = true;
|
||||||
|
this.addSheetToAPI(this.spreadsheetId);
|
||||||
|
},
|
||||||
|
addSheetToAPI(sheetId) {
|
||||||
|
fetch(`${this.$store.state.API_ENDPOINT}/sheet/create`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${this.$store.state.access_token}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: sheetId,
|
||||||
|
name: this.sheetName,
|
||||||
|
group_id: this.group,
|
||||||
|
frequency: this.frequency,
|
||||||
|
})
|
||||||
|
}).then(async response => {
|
||||||
|
const j = await response.json();
|
||||||
|
if (response.status === 201) {
|
||||||
|
this.showSnackbar(`Sheet created successfully!`, "green");
|
||||||
|
// this.$store.dispatch("refreshDocs"); //TODO: implement this
|
||||||
|
} else {
|
||||||
|
throw new Error(JSON.stringify(j));
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error("/sheet/create ", error);
|
||||||
|
this.showSnackbar(`Unable to save sheet to DB: ${error.message}`);
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
this.sheetName = "";
|
||||||
|
this.sheetUrlId = "";
|
||||||
|
this.group = "please select";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
161
src/components/ArchiveSheet.vue
Normal file
161
src/components/ArchiveSheet.vue
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
<template>
|
||||||
|
<v-container class="pane">
|
||||||
|
<v-card class="pa-3">
|
||||||
|
|
||||||
|
<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-tab v-for="item in items" :key="item" :text="item" :value="item"></v-tab>
|
||||||
|
</v-tabs>
|
||||||
|
|
||||||
|
<v-tabs-window v-model="tab" class="elevation-1 rounded">
|
||||||
|
<v-tabs-window-item :value="items[0]">
|
||||||
|
<v-card-text>
|
||||||
|
<AddSheet :actionIsCreate="true" />
|
||||||
|
<v-expansion-panels elevation="0">
|
||||||
|
<v-expansion-panel>
|
||||||
|
<v-expansion-panel-title>Instructions</v-expansion-panel-title>
|
||||||
|
<v-expansion-panel-text>
|
||||||
|
<ol>
|
||||||
|
<li>Choose a sheet name;</li>
|
||||||
|
<li>Choose a group: this will impact where/how to archive;</li>
|
||||||
|
<li>Choose a frequency: how often to archive;</li>
|
||||||
|
<li>Press "create" and wait;</li>
|
||||||
|
<li>Sheet will appear in "Your Sheets" below.</li>
|
||||||
|
</ol>
|
||||||
|
<small>
|
||||||
|
<b>NB:</b> This new sheet will be shared with the
|
||||||
|
service account necessary for Bellingcat's archiving
|
||||||
|
server.
|
||||||
|
</small>
|
||||||
|
</v-expansion-panel-text>
|
||||||
|
</v-expansion-panel>
|
||||||
|
</v-expansion-panels>
|
||||||
|
</v-card-text>
|
||||||
|
</v-tabs-window-item>
|
||||||
|
<v-tabs-window-item :value="items[1]">
|
||||||
|
<v-card-text>
|
||||||
|
<AddSheet :actionIsCreate="false" />
|
||||||
|
<v-expansion-panels elevation="0">
|
||||||
|
<v-expansion-panel>
|
||||||
|
<v-expansion-panel-title>Instructions</v-expansion-panel-title>
|
||||||
|
<v-expansion-panel-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>
|
||||||
|
<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>Choose a name to associate with this Sheet</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-expansion-panel-text>
|
||||||
|
</v-expansion-panel>
|
||||||
|
</v-expansion-panels>
|
||||||
|
</v-card-text>
|
||||||
|
</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>
|
||||||
|
import AddSheet from "@/components/AddSheet.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ArchiveSheet",
|
||||||
|
components: {
|
||||||
|
AddSheet
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tab: '',
|
||||||
|
items: ['Create new Archiver Sheet', 'Add existing Sheets'],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
user() {
|
||||||
|
return this.$store.state.user;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -54,7 +54,8 @@ export default createStore({
|
|||||||
docs: [],
|
docs: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
errorMessage: "",
|
errorMessage: "",
|
||||||
API_ENDPOINT: "https://auto-archiver-api.bellingcat.com"
|
// API_ENDPOINT: "https://auto-archiver-api.bellingcat.com"
|
||||||
|
API_ENDPOINT: "http://localhost:8004"
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setUser(state, user) {
|
setUser(state, user) {
|
||||||
@@ -64,6 +65,10 @@ export default createStore({
|
|||||||
setUserActiveState(state, active) {
|
setUserActiveState(state, active) {
|
||||||
state.user.active = active;
|
state.user.active = active;
|
||||||
},
|
},
|
||||||
|
setUserGroups(state, groups) {
|
||||||
|
state.user.groups = groups;
|
||||||
|
saveToLocalStorage(state);
|
||||||
|
},
|
||||||
setDocs(state, docs) {
|
setDocs(state, docs) {
|
||||||
state.docs = docs;
|
state.docs = docs;
|
||||||
},
|
},
|
||||||
@@ -89,6 +94,7 @@ export default createStore({
|
|||||||
|
|
||||||
commit("setUser", response.user);
|
commit("setUser", response.user);
|
||||||
dispatch("checkActiveUser");
|
dispatch("checkActiveUser");
|
||||||
|
dispatch("checkUserGroups");
|
||||||
dispatch("getDocs");
|
dispatch("getDocs");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +154,27 @@ export default createStore({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async checkUserGroups({ state, commit }) {
|
||||||
|
try {
|
||||||
|
commit("setErrorMessage", "");
|
||||||
|
const r = await fetch(
|
||||||
|
`${state.API_ENDPOINT}/groups`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${state.access_token}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const response = await r.json();
|
||||||
|
commit("setUserGroups", response);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("checkUserGroups (firebase.js): ", error);
|
||||||
|
commit("setErrorMessage", "Unable to fetch user groups from the API");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async getDocs({ state, commit }) {
|
async getDocs({ state, commit }) {
|
||||||
if (!state.user || !state.user.active) {
|
if (!state.user || !state.user.active) {
|
||||||
return;
|
return;
|
||||||
@@ -212,174 +239,157 @@ export default createStore({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async add({ state, dispatch, commit }, { name }) {
|
async add({ state, dispatch, commit }, name) {
|
||||||
commit("setLoading", true);
|
commit("setLoading", true);
|
||||||
|
|
||||||
try {
|
return new Promise(async (resolve, reject) => {
|
||||||
// create new sheet
|
try {
|
||||||
const newSheet = await gapi.client.sheets.spreadsheets.create({
|
// create new sheet
|
||||||
properties: {
|
const newSheet = await gapi.client.sheets.spreadsheets.create({
|
||||||
title: name,
|
properties: {
|
||||||
},
|
title: name,
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const spreadsheetId = newSheet.result.spreadsheetId;
|
const spreadsheetId = newSheet.result.spreadsheetId;
|
||||||
|
|
||||||
const userEnteredFormat = {
|
const userEnteredFormat = {
|
||||||
textFormat: {
|
textFormat: {
|
||||||
bold: true,
|
bold: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// add header row
|
// add header row
|
||||||
await gapi.client.sheets.spreadsheets.batchUpdate(
|
await gapi.client.sheets.spreadsheets.batchUpdate(
|
||||||
{
|
{
|
||||||
spreadsheetId: spreadsheetId,
|
spreadsheetId: spreadsheetId,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requests: [
|
requests: [
|
||||||
{
|
{
|
||||||
updateCells: {
|
updateCells: {
|
||||||
rows: [
|
rows: [
|
||||||
{
|
{
|
||||||
values: [
|
values: [
|
||||||
{
|
{
|
||||||
userEnteredValue: {
|
userEnteredValue: {
|
||||||
stringValue: "Link",
|
stringValue: "Link",
|
||||||
|
},
|
||||||
|
userEnteredFormat,
|
||||||
},
|
},
|
||||||
userEnteredFormat,
|
{
|
||||||
},
|
userEnteredValue: {
|
||||||
{
|
stringValue: "Archive status",
|
||||||
userEnteredValue: {
|
},
|
||||||
stringValue: "Archive status",
|
userEnteredFormat,
|
||||||
},
|
},
|
||||||
userEnteredFormat,
|
{
|
||||||
},
|
userEnteredValue: {
|
||||||
|
stringValue: "Destination folder",
|
||||||
{
|
},
|
||||||
userEnteredValue: {
|
userEnteredFormat,
|
||||||
stringValue: "Destination folder",
|
|
||||||
},
|
},
|
||||||
userEnteredFormat,
|
{
|
||||||
},
|
userEnteredValue: {
|
||||||
{
|
stringValue: "Archive location",
|
||||||
userEnteredValue: {
|
},
|
||||||
stringValue: "Archive location",
|
userEnteredFormat,
|
||||||
},
|
},
|
||||||
userEnteredFormat,
|
{
|
||||||
},
|
userEnteredValue: {
|
||||||
{
|
stringValue: "Archive date",
|
||||||
userEnteredValue: {
|
},
|
||||||
stringValue: "Archive date",
|
userEnteredFormat,
|
||||||
},
|
},
|
||||||
userEnteredFormat,
|
{
|
||||||
},
|
userEnteredValue: {
|
||||||
{
|
stringValue: "Thumbnail",
|
||||||
userEnteredValue: {
|
},
|
||||||
stringValue: "Thumbnail",
|
userEnteredFormat,
|
||||||
},
|
},
|
||||||
userEnteredFormat,
|
{
|
||||||
},
|
userEnteredValue: {
|
||||||
{
|
stringValue: "Upload timestamp",
|
||||||
userEnteredValue: {
|
},
|
||||||
stringValue: "Upload timestamp",
|
userEnteredFormat,
|
||||||
},
|
},
|
||||||
userEnteredFormat,
|
{
|
||||||
},
|
userEnteredValue: {
|
||||||
{
|
stringValue: "Upload title",
|
||||||
userEnteredValue: {
|
},
|
||||||
stringValue: "Upload title",
|
userEnteredFormat,
|
||||||
},
|
},
|
||||||
userEnteredFormat,
|
{
|
||||||
},
|
userEnteredValue: {
|
||||||
{
|
stringValue: "Textual content",
|
||||||
userEnteredValue: {
|
},
|
||||||
stringValue: "Textual content",
|
userEnteredFormat,
|
||||||
},
|
},
|
||||||
userEnteredFormat,
|
{
|
||||||
},
|
userEnteredValue: {
|
||||||
{
|
stringValue: "Screenshot",
|
||||||
userEnteredValue: {
|
},
|
||||||
stringValue: "Screenshot",
|
userEnteredFormat,
|
||||||
},
|
},
|
||||||
userEnteredFormat,
|
{
|
||||||
},
|
userEnteredValue: {
|
||||||
{
|
stringValue: "Hash",
|
||||||
userEnteredValue: {
|
},
|
||||||
stringValue: "Hash",
|
userEnteredFormat,
|
||||||
},
|
},
|
||||||
userEnteredFormat,
|
],
|
||||||
},
|
},
|
||||||
// {
|
],
|
||||||
// userEnteredValue: {
|
fields:
|
||||||
// stringValue: "WACZ",
|
"userEnteredValue.stringValue,userEnteredFormat.textFormat.bold",
|
||||||
// },
|
start: {
|
||||||
// userEnteredFormat,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// userEnteredValue: {
|
|
||||||
// stringValue: "Replaywebpage",
|
|
||||||
// },
|
|
||||||
// userEnteredFormat,
|
|
||||||
// },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
fields:
|
|
||||||
"userEnteredValue.stringValue,userEnteredFormat.textFormat.bold",
|
|
||||||
start: {
|
|
||||||
sheetId: 0,
|
|
||||||
rowIndex: 0,
|
|
||||||
columnIndex: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
addProtectedRange: {
|
|
||||||
protectedRange: {
|
|
||||||
range: {
|
|
||||||
sheetId: 0,
|
sheetId: 0,
|
||||||
startRowIndex: 0,
|
rowIndex: 0,
|
||||||
endRowIndex: 1,
|
columnIndex: 0,
|
||||||
startColumnIndex: 0,
|
|
||||||
endColumnIndex: 11,
|
|
||||||
},
|
},
|
||||||
description:
|
|
||||||
"Protecting header row (needed for auto-archiver), do not modify archiving column names, you can add and move columns around when no 'Archive in Progress' is present in the 'Archive status' column.",
|
|
||||||
warningOnly: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
],
|
addProtectedRange: {
|
||||||
|
protectedRange: {
|
||||||
|
range: {
|
||||||
|
sheetId: 0,
|
||||||
|
startRowIndex: 0,
|
||||||
|
endRowIndex: 1,
|
||||||
|
startColumnIndex: 0,
|
||||||
|
endColumnIndex: 11,
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
"Protecting header row (needed for auto-archiver), do not modify archiving column names, you can add and move columns around when no 'Archive in Progress' is present in the 'Archive status' column.",
|
||||||
|
warningOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// add permissions
|
||||||
|
await gapi.client.drive.permissions.create({
|
||||||
|
fileId: spreadsheetId,
|
||||||
|
resource: {
|
||||||
|
role: "writer",
|
||||||
|
type: "user",
|
||||||
|
emailAddress:
|
||||||
|
"bellingcat-auto-archiver-api@bellingcat-auto-archiver-b85db.iam.gserviceaccount.com",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve({ success: true, result: spreadsheetId });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("add (firebase.js): ", error);
|
||||||
|
if (error.status === 401) {
|
||||||
|
await dispatch("signout");
|
||||||
}
|
}
|
||||||
);
|
reject({ success: false, result: error });
|
||||||
|
}
|
||||||
// add permissions
|
commit("setLoading", false);
|
||||||
await gapi.client.drive.permissions.create({
|
});
|
||||||
fileId: spreadsheetId,
|
|
||||||
resource: {
|
|
||||||
role: "writer",
|
|
||||||
type: "user",
|
|
||||||
emailAddress:
|
|
||||||
"bellingcat-auto-archiver-api@bellingcat-auto-archiver-b85db.iam.gserviceaccount.com",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const col = await collection(firebaseFirestore, "sheets");
|
|
||||||
await addDoc(col, {
|
|
||||||
sheetId: spreadsheetId,
|
|
||||||
url: newSheet.result.spreadsheetUrl,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
uid: state.user.uid,
|
|
||||||
email: state.user.email,
|
|
||||||
lastArchived: null,
|
|
||||||
name: name,
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch("getDocs");
|
|
||||||
} catch (error) {
|
|
||||||
console.error("add (firebase.js): ", error);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async enable({ state, dispatch, commit }, { spreadsheetId }) {
|
async enable({ state, dispatch, commit }, { spreadsheetId }) {
|
||||||
@@ -456,7 +466,9 @@ export default createStore({
|
|||||||
if (expired) {
|
if (expired) {
|
||||||
store.dispatch("signout");
|
store.dispatch("signout");
|
||||||
} else {
|
} else {
|
||||||
|
//TODO: merge these into single endpoint in the future
|
||||||
store.dispatch("checkActiveUser");
|
store.dispatch("checkActiveUser");
|
||||||
|
store.dispatch("checkUserGroups");
|
||||||
store.dispatch("getDocs");
|
store.dispatch("getDocs");
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<ArchiveSheet v-if="user?.active" />
|
||||||
<v-container class="pane">
|
<v-container class="pane">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
<v-card style="margin-bottom: 1em">
|
<v-card style="margin-bottom: 1em">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-alert color="#f2d97c" icon="mdi-alert">
|
<v-alert color="#f2d97c" icon="mdi-alert">
|
||||||
This is a pre-release prototype demo service provided on a
|
This is still a pre-release prototype demo service provided on a
|
||||||
best-effort basis. Do not use for mission critical or sensitive
|
best-effort basis. Do not use for mission critical or sensitive
|
||||||
data.
|
data.
|
||||||
</v-alert>
|
</v-alert>
|
||||||
@@ -161,11 +162,12 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DocList from "@/components/DocList.vue";
|
import DocList from "@/components/DocList.vue";
|
||||||
|
import ArchiveSheet from "@/components/ArchiveSheet.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "SheetView",
|
name: "SheetView",
|
||||||
components: {
|
components: {
|
||||||
DocList,
|
DocList, ArchiveSheet
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user