add bin/ and store secret in path

master
Yorick van Pelt 2019-05-18 19:59:44 +02:00
parent 4d5c11ce24
commit 67bdb9f84c
Signed by: yorick
GPG Key ID: A36E70F9DC014A15
4 changed files with 66 additions and 35 deletions

27
bin/skl-auto-payslip.js Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env node
const App = require('../index.js')
const sig = fn =>
fn.toString().split('\n')[0].replace(/^async /, '').replace(/ \{/, '')
const methods = App.prototype
// poor man's CLI
const [,, method, ...args] = process.argv
if (method in methods) {
if (args.length < methods[method].length) {
console.error("not enough arguments for method", sig(methods[method]))
} else {
App.authAll().then((auth) => {
const app = new App(auth)
app[method](...args)
})
}
} else {
if (method && method != 'help') console.log("unknown subcall", method)
console.log("valid methods:")
for(const realMethod of Object.getOwnPropertyNames(methods)) {
if (realMethod == 'constructor') continue
if (methods[realMethod])
console.log(" ", sig(methods[realMethod]))
}
process.exit(1)
}

3
default.nix Normal file
View File

@ -0,0 +1,3 @@
with import <nixpkgs> {};
with callPackage (fetchTarball https://github.com/serokell/nix-npm-buildpackage/archive/master.tar.gz) {};
buildNpmPackage { src = ./.; }

View File

@ -8,19 +8,22 @@ const {promisify} = require('util')
const readFile = promisify(fs.readFile)
const base64url = require('base64url')
const process = require('process')
const path = require('path')
const exec = promisify(require('child_process').exec)
const {spawn} = require('child_process')
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/drive','https://www.googleapis.com/auth/gmail.compose'];
const USER = process.env['USER']
const HOME = process.env['HOME']
const UserCapitalized = USER.charAt(0).toUpperCase() + USER.slice(1)
const config = {
tokenPath: 'token.json',
fileId: "1CqUglyFNoEWL0lx-e7arqUGOOZEYW__JjcwBmNaXE6E",
template: `${UserCapitalized} ${(new Date()).getFullYear()} Template`,
to: "billing@serokell.io",
destdir: "payslips"
destdir: `${HOME}/serokell/invoices/payslips`
}
async function authAll() {
@ -36,7 +39,7 @@ async function authorize(credentials) {
// Check if we have previously stored a token.
let token
try {
token = await readFile(config.tokenPath)
token = await pass_get("skl-auto-payslip")
} catch(e) {
return getAccessToken(oAuth2Client)
}
@ -53,6 +56,21 @@ async function ask(question) {
rl.close()
return answer
}
function pass_insert(name, val) {
return new Promise((resolve, reject) => {
const pass = spawn("pass", ['insert', 'skl-auto-payslip', '-m'], {stdio: ['pipe', 'inherit', 'inherit']})
pass.stdin.write(val)
pass.stdin.end()
pass.on('close', (code) => {
if (code != 0) reject(`pass exited with code ${code}`)
else { resolve() }
})
})
}
async function pass_get(name) {
const {stdout, stderr} = await exec("pass skl-auto-payslip")
return stdout
}
async function getAccessToken(oAuth2Client) {
const authUrl = oAuth2Client.generateAuthUrl({
@ -63,10 +81,10 @@ async function getAccessToken(oAuth2Client) {
const code = await ask('Enter the code from that page here: ')
const token = (await oAuth2Client.getToken(code)).tokens
oAuth2Client.setCredentials(token);
await promisify(fs.writeFile)(config.tokenPath, JSON.stringify(token))
console.log('Token stored to', config.tokenPath);
await pass_insert('skl-auto-payslip', JSON.stringify(token))
return oAuth2Client
}
class InvoiceDocument {
constructor(auth, fileId) {
Object.assign(this, {
@ -171,16 +189,20 @@ The total is ${total} to ${iban} (same as always). Thanks!`
}
const datefmt = date =>
`${date.getFullYear()}-${(""+(date.getMonth()+1)).padStart(2, '0')}-${(""+date.getDate()).padStart(2, '0')}`
const methods = {
class App {
constructor(auth) {
this.auth = auth
this.doc = new InvoiceDocument(auth, config.fileId)
}
async export(sheetname, pdfOut) { // save sheet as pdf
console.log("Exporting", sheetname, "to", pdfOut)
const sheetId = await this.doc.sheetByName(sheetname)
return this.doc.sheetAsPdf(pdfOut, sheetId)
},
}
async create(sheetname, template=config.template) { // copy sheet from template
console.log("Creating", sheetname, "from", template)
return this.doc.copyTo(template, sheetname)
},
}
async update(sheetname, startDate, endDate, hours) { // set values in sheet
console.log("Setting data in", sheetname)
hours = +hours
@ -194,7 +216,7 @@ const methods = {
D16: values.regular, D17: values.overwork,
B9: values.startDate, D9: values.endDate
})
},
}
async draft(sheetname) { // export pdf+create gmail draft
const pdfname = `${config.destdir}/${sheetname}.pdf`
try {
@ -207,7 +229,7 @@ const methods = {
const iban = paid.replace(/^Paid to /, '')
console.log(paid, total)
composeDraft(sheetname, iban, total, pdfname, this.auth)
},
}
async auto(hours) { // just do everything
const date = new Date()
const day = date.getDate()
@ -244,31 +266,9 @@ const methods = {
}
await this.update(sheetname, startDate, endDate, hours)
await this.draft(sheetname)
},
}
async authenticate() { // just auth
}
}
const sig = fn =>
fn.toString().split('\n')[0].replace(/^async /, '').replace(/ \{/, '')
// poor man's CLI
const [,, method, ...args] = process.argv
if (method in methods) {
if (args.length < methods[method].length) {
console.error("not enough arguments for method", sig(methods[method]))
} else {
authAll().then((auth) => {
methods.auth = auth
methods.doc = new InvoiceDocument(auth, config.fileId)
methods[method](...args)
})
}
} else {
if (method && method != 'help') console.log("unknown subcall", method)
console.log("valid methods:")
for(const realMethod in methods) {
console.log(" ", sig(methods[realMethod]))
}
process.exit(1)
}
module.exports = App
module.exports.authAll = authAll

View File

@ -6,6 +6,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"bin": "bin/skl-auto-payslip.js",
"author": "",
"license": "ISC",
"dependencies": {