@@ -14,18 +14,23 @@ const path = require('path');
1414const fs = require ( 'fs' ) ;
1515const { URLSearchParams, URL } = require ( 'url' ) ;
1616const multer = require ( 'multer' ) ;
17+ const { DateTime } = require ( 'luxon' ) ;
1718
1819const app = express ( ) ;
1920const upload = multer ( ) ;
2021
2122const DEFAULT_THREADS_QUERY_LIMIT = 10 ;
2223
2324const FIELD__ALT_TEXT = 'alt_text' ;
25+ const FIELD__APPLICATION = 'application' ;
26+ const FIELD__APP_SCOPED_USER_ID = 'user_id' ;
2427const FIELD__CLICKS = 'clicks' ;
2528const FIELD__ERROR_MESSAGE = 'error_message' ;
29+ const FIELD__EXPIRES_AT = 'expires_at' ;
2630const FIELD__FOLLOWERS_COUNT = 'followers_count' ;
2731const FIELD__HIDE_STATUS = 'hide_status' ;
2832const FIELD__ID = 'id' ;
33+ const FIELD__ISSUED_AT = 'issued_at' ;
2934const FIELD__IS_REPLY = 'is_reply' ;
3035const FIELD__IS_VERIFIED = 'is_verified' ;
3136const FIELD__LIKES = 'likes' ;
@@ -40,6 +45,7 @@ const FIELD__REPLIES = 'replies';
4045const FIELD__REPOSTS = 'reposts' ;
4146const FIELD__QUOTES = 'quotes' ;
4247const FIELD__REPLY_AUDIENCE = 'reply_audience' ;
48+ const FIELD__SCOPES = 'scopes' ;
4349const FIELD__SHARES = 'shares' ;
4450const FIELD__STATUS = 'status' ;
4551const FIELD__TEXT = 'text' ;
@@ -63,6 +69,7 @@ const PARAMS__DELETE_CONFIG = 'delete_config';
6369const PARAMS__DELETE_QUOTA_USAGE = 'delete_quota_usage' ;
6470const PARAMS__FIELDS = 'fields' ;
6571const PARAMS__HIDE = 'hide' ;
72+ const PARAMS__INPUT_TOKEN = 'input_token' ;
6673const PARAMS__LINK_ATTACHMENT = 'link_attachment' ;
6774const PARAMS__LOCATION_SEARCH_CONFIG = 'location_search_config' ;
6875const PARAMS__LOCATION_SEARCH_QUOTA_USAGE = 'location_search_quota_usage' ;
@@ -803,6 +810,37 @@ app.get('/mentions', loggedInUserChecker, async (req, res) => {
803810 } ) ;
804811} ) ;
805812
813+ app . get ( '/debug' , loggedInUserChecker , async ( req , res ) => {
814+ const params = {
815+ [ PARAMS__INPUT_TOKEN ] : req . session . access_token ,
816+ } ;
817+
818+ const debugAccessTokenUrl = buildGraphAPIURL ( `debug_token` , params , req . session . access_token ) ;
819+
820+ let data = { } ;
821+ try {
822+ const response = await axios . get ( debugAccessTokenUrl , { httpsAgent : agent } ) ;
823+ data = response . data . data ;
824+ } catch ( e ) {
825+ console . error ( e ?. response ?. data ?. error ?. message ?? e . message ) ;
826+ }
827+
828+ const applicationName = data [ FIELD__APPLICATION ] ;
829+ const expiresAt = formatTimestamp ( data [ FIELD__EXPIRES_AT ] ) ;
830+ const issuedAt = formatTimestamp ( data [ FIELD__ISSUED_AT ] ) ;
831+ const scopes = data [ FIELD__SCOPES ] . join ( ', ' ) ;
832+ const appScopedUserId = data [ FIELD__APP_SCOPED_USER_ID ] ;
833+
834+ return res . render ( 'debug' , {
835+ title : 'Inspect Access Token' ,
836+ applicationName,
837+ expiresAt,
838+ issuedAt,
839+ scopes,
840+ appScopedUserId,
841+ } ) ;
842+ } ) ;
843+
806844app . get ( '/keywordSearch' , loggedInUserChecker , async ( req , res ) => {
807845 const { keyword, searchType } = req . query ;
808846
@@ -988,6 +1026,15 @@ function addAttachmentFields(target, attachmentType, url, altText) {
9881026 }
9891027}
9901028
1029+ /**
1030+ * @param {int } timestamp
1031+ */
1032+ function formatTimestamp ( timestamp ) {
1033+ const userTimeZone = Intl . DateTimeFormat ( ) . resolvedOptions ( ) . timeZone ;
1034+ return DateTime . fromSeconds ( timestamp , { zone : userTimeZone } )
1035+ . toLocaleString ( DateTime . DATETIME_FULL_WITH_SECONDS ) ;
1036+ }
1037+
9911038/**
9921039 * @param {URL } sourceUrl
9931040 * @param {URL } destinationUrl
0 commit comments