parent
e7a7b45ad0
commit
15fdb69b96
@ -25,5 +25,6 @@
|
|||||||
"SSL_KEY_FILE_PATH": "/etc/letsencrypt/live/mysite/privkey.pem",
|
"SSL_KEY_FILE_PATH": "/etc/letsencrypt/live/mysite/privkey.pem",
|
||||||
"BTCPAY_URL": "",
|
"BTCPAY_URL": "",
|
||||||
"BTCPAY_WEBHOOK_URL": "",
|
"BTCPAY_WEBHOOK_URL": "",
|
||||||
"BTCPAY_AUTH": ""
|
"BTCPAY_AUTH": "",
|
||||||
|
"TWITTER_BEARER_AUTH": ""
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,9 @@ class Donations {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
this.runMigration();
|
||||||
|
}
|
||||||
|
|
||||||
setNotfyDonationStatusCallback(fn: any) {
|
setNotfyDonationStatusCallback(fn: any) {
|
||||||
this.notifyDonationStatusCallback = fn;
|
this.notifyDonationStatusCallback = fn;
|
||||||
@ -65,20 +67,48 @@ class Donations {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let imageUrl = '';
|
let imageBlob = '';
|
||||||
let handle = '';
|
let handle = '';
|
||||||
|
let imageUrl = '';
|
||||||
|
let twitter_id = null;
|
||||||
if (response.orderId !== '') {
|
if (response.orderId !== '') {
|
||||||
try {
|
try {
|
||||||
const hiveData = await this.$getTwitterImageUrl(response.orderId);
|
const userData = await this.$getTwitterUserData(response.orderId);
|
||||||
imageUrl = hiveData.imageUrl;
|
imageUrl = userData.profile_image_url.replace('normal', '200x200');
|
||||||
handle = hiveData.screenName;
|
imageBlob = await this.$downloadProfileImageBlob(imageUrl);
|
||||||
|
handle = userData.screen_name;
|
||||||
|
twitter_id = userData.id;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('Error fetching twitter image' + e.message);
|
logger.err('Error fetching twitter data: ' + e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Creating database entry for donation with invoice id: ' + response.id);
|
logger.debug('Creating database entry for donation with invoice id: ' + response.id);
|
||||||
this.$addDonationToDatabase(response.btcPaid, handle, response.id, imageUrl);
|
this.$addDonationToDatabase(response.btcPaid, handle, twitter_id, response.id, imageUrl, imageBlob);
|
||||||
|
}
|
||||||
|
|
||||||
|
async $getDonationsFromDatabase() {
|
||||||
|
try {
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
const query = `SELECT handle, imageUrl, TO_BASE64(image) AS image_64 FROM donations WHERE handle != '' ORDER BY id DESC`;
|
||||||
|
const [rows] = await connection.query<any>(query);
|
||||||
|
connection.release();
|
||||||
|
return rows;
|
||||||
|
} catch (e) {
|
||||||
|
logger.err('$getDonationsFromDatabase() error' + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $getLegacyDonations() {
|
||||||
|
try {
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
const query = `SELECT * FROM donations WHERE twitter_id IS NULL AND handle != ''`;
|
||||||
|
const [rows] = await connection.query<any>(query);
|
||||||
|
connection.release();
|
||||||
|
return rows;
|
||||||
|
} catch (e) {
|
||||||
|
logger.err('$getLegacyDonations() error' + e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getStatus(id: string): Promise<any> {
|
private getStatus(id: string): Promise<any> {
|
||||||
@ -96,27 +126,18 @@ class Donations {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async $getDonationsFromDatabase() {
|
private async $addDonationToDatabase(btcPaid: number, handle: string, twitter_id: number | null,
|
||||||
|
orderId: string, imageUrl: string, image: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const connection = await DB.pool.getConnection();
|
const connection = await DB.pool.getConnection();
|
||||||
const query = `SELECT handle, imageUrl FROM donations WHERE handle != '' ORDER BY id DESC`;
|
const query = `INSERT IGNORE INTO donations(added, amount, handle, twitter_id, order_id, imageUrl, image) VALUES (NOW(), ?, ?, ?, ?, ?, FROM_BASE64(?))`;
|
||||||
const [rows] = await connection.query<any>(query);
|
const params: (string | number | null)[] = [
|
||||||
connection.release();
|
|
||||||
return rows;
|
|
||||||
} catch (e) {
|
|
||||||
logger.err('$getDonationsFromDatabase() error' + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async $addDonationToDatabase(btcPaid: number, handle: string, orderId: string, imageUrl: string): Promise<void> {
|
|
||||||
try {
|
|
||||||
const connection = await DB.pool.getConnection();
|
|
||||||
const query = `INSERT IGNORE INTO donations(added, amount, handle, order_id, imageUrl) VALUES (NOW(), ?, ?, ?, ?)`;
|
|
||||||
const params: (string | number)[] = [
|
|
||||||
btcPaid,
|
btcPaid,
|
||||||
handle,
|
handle,
|
||||||
|
twitter_id,
|
||||||
orderId,
|
orderId,
|
||||||
imageUrl,
|
imageUrl,
|
||||||
|
image,
|
||||||
];
|
];
|
||||||
const [result]: any = await connection.query(query, params);
|
const [result]: any = await connection.query(query, params);
|
||||||
connection.release();
|
connection.release();
|
||||||
@ -125,19 +146,69 @@ class Donations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async $getTwitterImageUrl(handle: string): Promise<any> {
|
private async $updateDonation(id: number, handle: string, twitterId: number, imageUrl: string, image: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
const query = `UPDATE donations SET handle = ?, twitter_id = ?, imageUrl = ?, image = FROM_BASE64(?) WHERE id = ?`;
|
||||||
|
const params: (string | number)[] = [
|
||||||
|
handle,
|
||||||
|
twitterId,
|
||||||
|
imageUrl,
|
||||||
|
image,
|
||||||
|
id,
|
||||||
|
];
|
||||||
|
const [result]: any = await connection.query(query, params);
|
||||||
|
connection.release();
|
||||||
|
} catch (e) {
|
||||||
|
logger.err('$updateDonation() error' + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $getTwitterUserData(handle: string): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
logger.debug('Fetching Hive.one data...');
|
logger.debug('Fetching Twitter API data...');
|
||||||
request.get({
|
request.get({
|
||||||
uri: `https://api.hive.one/v1/influencers/screen_name/${handle}/?format=json`,
|
uri: `https://api.twitter.com/1.1/users/show.json?screen_name=${handle}`,
|
||||||
json: true,
|
json: true,
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + config.TWITTER_BEARER_AUTH
|
||||||
|
},
|
||||||
}, (err, res, body) => {
|
}, (err, res, body) => {
|
||||||
if (err) { return reject(err); }
|
if (err) { return reject(err); }
|
||||||
logger.debug('Hive.one data fetched:' + JSON.stringify(body.data));
|
logger.debug('Twitter user data fetched:' + JSON.stringify(body.data));
|
||||||
resolve(body.data);
|
resolve(body);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async $downloadProfileImageBlob(url: string): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
logger.debug('Fetching image blob...');
|
||||||
|
request.get({
|
||||||
|
uri: url,
|
||||||
|
encoding: null,
|
||||||
|
}, (err, res, body) => {
|
||||||
|
if (err) { return reject(err); }
|
||||||
|
logger.debug('Image downloaded.');
|
||||||
|
resolve(Buffer.from(body, 'utf8').toString('base64'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async runMigration() {
|
||||||
|
const legacyDonations = await this.$getLegacyDonations();
|
||||||
|
legacyDonations.forEach(async (donation: any) => {
|
||||||
|
logger.debug('Migrating donation for handle: ' + donation.handle);
|
||||||
|
try {
|
||||||
|
const twitterData = await this.$getTwitterUserData(donation.handle);
|
||||||
|
const imageUrl = twitterData.profile_image_url.replace('normal', '200x200');
|
||||||
|
const imageBlob = await this.$downloadProfileImageBlob(imageUrl);
|
||||||
|
await this.$updateDonation(donation.id, twitterData.screen_name, twitterData.id, imageUrl, imageBlob);
|
||||||
|
} catch (e) {
|
||||||
|
logger.err('Failed to migrate donation for handle: ' + donation.handle + '. ' + (e.message || e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Donations();
|
export default new Donations();
|
||||||
|
@ -44,7 +44,9 @@
|
|||||||
|
|
||||||
<ng-template ngFor let-sponsor [ngForOf]="sponsors">
|
<ng-template ngFor let-sponsor [ngForOf]="sponsors">
|
||||||
<a [href]="'https://twitter.com/' + sponsor.handle" target="_blank">
|
<a [href]="'https://twitter.com/' + sponsor.handle" target="_blank">
|
||||||
<div class="profile_photo d-inline-block" [ngStyle]="{'background-image': 'url(' + sponsor.imageUrl + ')'}" [title]="sponsor.handle"></div>
|
<div class="profile_photo d-inline-block" [title]="sponsor.handle">
|
||||||
|
<img class="profile_img" [src]="bypassSecurityTrustUrl('data:image/jpeg;base64,' + sponsor.image_64)" />
|
||||||
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<br><br>
|
<br><br>
|
||||||
@ -82,7 +84,7 @@
|
|||||||
|
|
||||||
<div *ngIf="donationStatus === 3" class="text-center">
|
<div *ngIf="donationStatus === 3" class="text-center">
|
||||||
<div class="qr-wrapper mt-2 mb-2">
|
<div class="qr-wrapper mt-2 mb-2">
|
||||||
<a [href]="bitcoinUrl" target="_blank">
|
<a [href]="bypassSecurityTrustUrl('bitcoin:' + donationObj.address + '?amount=' + donationObj.amount)" target="_blank">
|
||||||
<app-qrcode [data]="'bitcoin:' + donationObj.address + '?amount=' + donationObj.amount"></app-qrcode>
|
<app-qrcode [data]="'bitcoin:' + donationObj.address + '?amount=' + donationObj.amount"></app-qrcode>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,6 +12,13 @@
|
|||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.profile_img {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.text-small {
|
.text-small {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ export class AboutComponent implements OnInit {
|
|||||||
donationObj: any;
|
donationObj: any;
|
||||||
sponsorsEnabled = env.SPONSORS_ENABLED;
|
sponsorsEnabled = env.SPONSORS_ENABLED;
|
||||||
sponsors = null;
|
sponsors = null;
|
||||||
bitcoinUrl: SafeUrl;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
@ -63,8 +62,11 @@ export class AboutComponent implements OnInit {
|
|||||||
.subscribe((response) => {
|
.subscribe((response) => {
|
||||||
this.websocketService.trackDonation(response.id);
|
this.websocketService.trackDonation(response.id);
|
||||||
this.donationObj = response;
|
this.donationObj = response;
|
||||||
this.bitcoinUrl = this.sanitizer.bypassSecurityTrustUrl('bitcoin:' + this.donationObj.address + '?amount=' + this.donationObj.amount);
|
|
||||||
this.donationStatus = 3;
|
this.donationStatus = 3;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bypassSecurityTrustUrl(text: string): SafeUrl {
|
||||||
|
return this.sanitizer.bypassSecurityTrustUrl(text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,3 +102,6 @@ ALTER TABLE `donations`
|
|||||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
||||||
|
|
||||||
ALTER TABLE `donations` ADD UNIQUE(`order_id`);
|
ALTER TABLE `donations` ADD UNIQUE(`order_id`);
|
||||||
|
|
||||||
|
ALTER TABLE `donations` ADD `image` BLOB NULL AFTER `imageUrl`;
|
||||||
|
ALTER TABLE `donations` ADD `twitter_id` INT NULL AFTER `handle`;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user