Add cron functions; improve styles

This commit is contained in:
Logan Williams
2023-05-20 15:12:03 +02:00
parent 14fa178d49
commit 0a2923bfde
18 changed files with 16623 additions and 4726 deletions

View File

@@ -1,7 +1,7 @@
index.html,1684481624309,77e9dacd8a347223c5beb3b555f99692ca0c4d6ca5f09b8787df763406de0c08
js/app.537ca05d.js,1684481624308,e7cc5519a0c32d10bde9da4dd8ac32a560c171f63bcec0349aa72d3b5a3f77a3
favicon.ico,1684481624308,1e71457865f706dc865b49a54a86e193818220d290b30226b6630a42faf1535d
js/app.537ca05d.js.map,1684481624309,a191afff69155541073af65e5bb1bae76bacc3b0fc07a3d5fc53947f8529f401
css/chunk-vendors.07681150.css,1684481624309,c7bd88012597ed0484687de4fc4645d327cb5b3511a983d726f5f147071c1ab9
js/chunk-vendors.18403580.js,1684481624309,4e4b4f9e77b250f08186024fa36f7b7ca76c39d9a0da0f55316638254df0c344
js/chunk-vendors.18403580.js.map,1684481624309,1fbadcea2893e3ec3db735bd837e34254ca6a2fedabbd4717fc0b64419a3dff0
index.html,1684519817551,086477abf422bc18a7be7bf043c9802b30755ccb51462c4e9a091c0b1426df6e
js/app.e05e8bdc.js,1684519817551,f253c0a917c3b6f89660b6ecf9e029f04df4c482e19836b5e3caa05a9d8eda98
favicon.ico,1684519817550,1e71457865f706dc865b49a54a86e193818220d290b30226b6630a42faf1535d
js/app.e05e8bdc.js.map,1684519817550,c7da86629647b686a40a70eb485fdd3c7fc46abc89280c5d6592d2536017fef2
css/chunk-vendors.07681150.css,1684519817551,c7bd88012597ed0484687de4fc4645d327cb5b3511a983d726f5f147071c1ab9
js/chunk-vendors.ca3d37d3.js,1684519817551,7158a8336b70e67f09cf172db176eaf861e8d49133b873a6024cb022d781724e
js/chunk-vendors.ca3d37d3.js.map,1684519817550,45ba022f977fda2960121c185b9a4199f8258f90bb16749ea8f783586ae91374

View File

@@ -12,5 +12,20 @@
"destination": "/index.html"
}
]
}
},
"functions": [
{
"source": "functions",
"codebase": "default",
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log"
],
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
]
}

28
functions/.eslintrc.js Normal file
View File

@@ -0,0 +1,28 @@
module.exports = {
env: {
es6: true,
node: true,
},
parserOptions: {
"ecmaVersion": 2018,
requireConfigFile: false,
},
extends: [
"google",
],
rules: {
"no-restricted-globals": ["error", "name", "length"],
"prefer-arrow-callback": "error",
"quotes": ["error", "double", {"allowTemplateLiterals": true}],
},
overrides: [
{
files: ["**/*.spec.*"],
env: {
mocha: true,
},
rules: {},
},
],
globals: {},
};

1
functions/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules/

57
functions/index.js Normal file
View File

@@ -0,0 +1,57 @@
/**
* Import function triggers from their respective submodules:
*
* const {onCall} = require("firebase-functions/v2/https");
* const {onDocumentWritten} = require("firebase-functions/v2/firestore");
*
* See a full list of supported triggers at https://firebase.google.com/docs/functions
*/
const { onSchedule } = require("firebase-functions/v2/scheduler");
const logger = require("firebase-functions/logger");
// The Firebase Admin SDK to access Firestore.
const { initializeApp } = require("firebase-admin/app");
const { getFirestore } = require("firebase-admin/firestore");
initializeApp();
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
exports.processSheetScheduler = onSchedule(
"0,15,30,45 * * * *",
async (event) => {
const db = getFirestore();
// get all documents from firestore sheets collection
const querySnapshot = await db.collection("sheets").get();
querySnapshot.forEach(async (doc) => {
console.log("processing document: ", doc.id);
// send POST request with sheetID to trigger sheet processing
const sheetId = doc.data().sheetId;
const url = "https://auto-archiver-api.bellingcat.com/sheet_service";
const data = { sheet_id: sheetId };
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization:
"Basic " +
Buffer.from(
"service:password"
).toString("base64"),
},
body: JSON.stringify(data),
};
const response = await fetch(url, options);
console.log(response);
await doc.ref.update({ lastArchived: Date.now() });
await sleep(1000);
});
}
);

11871
functions/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

26
functions/package.json Normal file
View File

@@ -0,0 +1,26 @@
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "18"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^11.8.0",
"firebase-functions": "^4.3.1"
},
"devDependencies": {
"eslint": "^8.15.0",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^3.1.0"
},
"private": true
}

View File

@@ -8,6 +8,7 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@mdi/font": "^7.2.96",
"core-js": "^3.8.3",
"firebase": "^9.22.0",
"firebaseui": "^6.0.2",

View File

@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<title>Bellingcat Auto Archiver</title>
<script src="https://accounts.google.com/gsi/client" async defer></script>
</head>
<body>

View File

@@ -1,10 +1,12 @@
<template>
<v-app>
<NavBar />
<v-main>
<router-view />
</v-main>
</v-app>
<div class="pane">
<v-app>
<NavBar />
<v-main>
<router-view />
</v-main>
</v-app>
</div>
</template>
<script>
@@ -18,4 +20,16 @@ export default {
};
</script>
<style></style>
<style>
.pane {
max-width: 800px;
margin-left: auto;
margin-right: auto;
height: 100vh;
box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.25);
}
html {
background-color: #d6e8de;
}
</style>

View File

@@ -13,44 +13,45 @@
: "never"
}}
</div>
<div class="doc-id">ID: {{ doc.sheetId }}</div>
</v-card-text>
<v-card-actions>
<v-col>
<v-btn :href="doc.url" target="_blank"
>Open sheet
<v-icon>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="{ on, attrs }">
<v-col class="text-right">
<v-btn color="red lighten-2" right v-bind="attrs" v-on="on"
>Stop archiving</v-btn
>
</v-col>
</template>
<v-row>
<v-col>
<v-btn :href="doc.url" target="_blank"
>Open sheet
<v-icon 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="{ on, attrs }">
<v-col class="text-right">
<v-btn color="#f2d97c" right v-bind="attrs" v-on="on"
>Stop archiving</v-btn
>
</v-col>
</template>
<v-card>
<v-card-title>Stop archiving "{{ doc.name }}"? </v-card-title>
<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-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-divider></v-divider>
<v-card-actions>
<v-btn @click="dialog = false" color="primary">Cancel</v-btn>
<v-spacer></v-spacer>
<v-btn color="red" text @click="remove"> Stop archiving </v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-card-actions>
<v-btn @click="dialog = false" color="primary">Cancel</v-btn>
<v-spacer></v-spacer>
<v-btn color="red" text @click="remove"> Stop archiving </v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</v-card-actions>
</v-card>
</template>

View File

@@ -1,16 +1,16 @@
<template>
<v-app-bar style="flex-grow: 0" class="text-no-wrap">
<v-toolbar-title>Bellingcat auto archiver setup tool</v-toolbar-title>
<v-toolbar-title>Bellingcat Auto Archiver demo tool</v-toolbar-title>
<v-spacer></v-spacer>
<v-col class="nav-wrapper">
<v-btn v-if="!user" @click="$store.dispatch('signin')">Sign In</v-btn>
<span class="user" v-if="user">
{{ user.email }}
</span>
<v-btn v-if="user" href="#" @click="$store.dispatch('signout')"
>Sign Out</v-btn
>
</v-col>
<!-- <v-col class="nav-wrapper"> -->
<v-btn v-if="!user" @click="$store.dispatch('signin')">Sign In</v-btn>
<span class="user" v-if="user">
{{ user.email }}
</span>
<v-btn v-if="user" href="#" @click="$store.dispatch('signout')"
>Sign Out</v-btn
>
<!-- </v-col> -->
</v-app-bar>
</template>
@@ -25,4 +25,9 @@ export default {
};
</script>
<style></style>
<style>
.user {
margin-right: 1em;
font-size: 80%;
}
</style>

View File

@@ -1,49 +0,0 @@
<template>
<div
class="card horizontal"
style="max-width: 400px; margin: 0 auto"
v-if="user"
>
<div class="card-image" style="margin-top: 25px; margin-left: 10px">
<img
:src="user.photoURL"
style="
width: 75px;
height: 75px;
border-radius: 50%;
border: 4px solid #333;
"
/>
</div>
<div class="card-stacked">
<div class="card-content">
<p>
name: <strong>{{ user.displayName }}</strong
><br />email:<strong>{{ user.email }}</strong
><br />uid: <strong>{{ user.uid }}</strong> <br />provider:
<strong class="teal-text">{{
user.providerData[0].providerId
}}</strong>
</p>
</div>
</div>
</div>
</template>
<script>
export default {
name: "ProfileView",
data() {
return {
items: [],
};
},
computed: {
user() {
return this.$store.state.user;
},
},
};
</script>
<style></style>

View File

@@ -4,6 +4,7 @@ import App from "./App.vue";
import router from "./router";
import store from "./store";
import "vuetify/dist/vuetify.min.css";
import "@mdi/font/css/materialdesignicons.css";
Vue.use(Vuetify);

View File

@@ -64,7 +64,7 @@ export default new Vuex.Store({
client_id:
"406209235111-r1mpkvkfaqc2jg5iqbvffl2b0rf4clbo.apps.googleusercontent.com",
scope:
"https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/userinfo.profile",
"https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
callback,
});
@@ -247,18 +247,18 @@ export default new Vuex.Store({
},
userEnteredFormat,
},
{
userEnteredValue: {
stringValue: "WACZ",
},
userEnteredFormat,
},
{
userEnteredValue: {
stringValue: "Replaywebpage",
},
userEnteredFormat,
},
// {
// userEnteredValue: {
// stringValue: "WACZ",
// },
// userEnteredFormat,
// },
// {
// userEnteredValue: {
// stringValue: "Replaywebpage",
// },
// userEnteredFormat,
// },
],
},
],
@@ -279,7 +279,7 @@ export default new Vuex.Store({
startRowIndex: 0,
endRowIndex: 1,
startColumnIndex: 0,
endColumnIndex: 13,
endColumnIndex: 11,
},
description:
"Protecting header row (needed for auto-archiver)",

View File

@@ -1,5 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>

View File

@@ -1,5 +1,5 @@
<template>
<v-container v-if="user">
<v-container>
<v-row>
<v-col>
<v-card>
@@ -11,8 +11,13 @@
the service account necessary for Bellingcat's archiving server.
You can modify and share the Google Sheet subsequently, but do not
edit the column names in the header row or remove the service
account from the shared users. For more information about the auto
archiver and how to use it, see
account from the shared users.
</p>
<p>
Links to online sources added to the "Link" column will be
archived every 15 minutes, or can be triggered manually below. 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
>
@@ -35,10 +40,16 @@
</v-card>
</v-col>
</v-row>
<DocList />
</v-container>
<v-container v-else>
<v-alert type="error">Sign in to set up an auto archiver</v-alert>
<DocList v-if="user" />
<v-row v-else
><v-col
><v-alert color="#f2d97c" light icon="mdi-alert"
>Sign in to set up an auto archiver</v-alert
></v-col
></v-row
>
</v-container>
</template>

9104
yarn.lock

File diff suppressed because it is too large Load Diff