@@ -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' ;
@@ -805,6 +812,37 @@ app.get('/mentions', loggedInUserChecker, async (req, res) => {
805812 } ) ;
806813} ) ;
807814
815+ app . get ( '/debug' , loggedInUserChecker , async ( req , res ) => {
816+ const params = {
817+ [ PARAMS__INPUT_TOKEN ] : req . session . access_token ,
818+ } ;
819+
820+ const debugAccessTokenUrl = buildGraphAPIURL ( `debug_token` , params , req . session . access_token ) ;
821+
822+ let data = { } ;
823+ try {
824+ const response = await axios . get ( debugAccessTokenUrl , { httpsAgent : agent } ) ;
825+ data = response . data . data ;
826+ } catch ( e ) {
827+ console . error ( e ?. response ?. data ?. error ?. message ?? e . message ) ;
828+ }
829+
830+ const applicationName = data [ FIELD__APPLICATION ] ;
831+ const expiresAt = formatTimestamp ( data [ FIELD__EXPIRES_AT ] ) ;
832+ const issuedAt = formatTimestamp ( data [ FIELD__ISSUED_AT ] ) ;
833+ const scopes = data [ FIELD__SCOPES ] . join ( ', ' ) ;
834+ const appScopedUserId = data [ FIELD__APP_SCOPED_USER_ID ] ;
835+
836+ return res . render ( 'debug' , {
837+ title : 'Inspect Access Token' ,
838+ applicationName,
839+ expiresAt,
840+ issuedAt,
841+ scopes,
842+ appScopedUserId,
843+ } ) ;
844+ } ) ;
845+
808846app . get ( '/keywordSearch' , loggedInUserChecker , async ( req , res ) => {
809847 const { keyword, searchType } = req . query ;
810848
@@ -1083,6 +1121,15 @@ function addAttachmentFields(target, attachmentType, url, altText) {
10831121 }
10841122}
10851123
1124+ /**
1125+ * @param {int } timestamp
1126+ */
1127+ function formatTimestamp ( timestamp ) {
1128+ const userTimeZone = Intl . DateTimeFormat ( ) . resolvedOptions ( ) . timeZone ;
1129+ return DateTime . fromSeconds ( timestamp , { zone : userTimeZone } )
1130+ . toLocaleString ( DateTime . DATETIME_FULL_WITH_SECONDS ) ;
1131+ }
1132+
10861133/**
10871134 * @param {URL } sourceUrl
10881135 * @param {URL } destinationUrl
0 commit comments