ghost 6.21.0 → 6.21.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/tryghost-i18n-6.21.2.tgz +0 -0
- package/components/{tryghost-parse-email-address-6.21.0.tgz → tryghost-parse-email-address-6.21.2.tgz} +0 -0
- package/content/themes/casper/LICENSE +1 -1
- package/content/themes/casper/README.md +7 -2
- package/content/themes/casper/assets/built/casper.js +1 -2
- package/content/themes/casper/assets/built/casper.js.map +1 -1
- package/content/themes/casper/assets/built/global.css +1 -2
- package/content/themes/casper/assets/built/global.css.map +1 -1
- package/content/themes/casper/assets/built/screen.css +1 -2
- package/content/themes/casper/assets/built/screen.css.map +1 -1
- package/content/themes/casper/assets/css/global.css +4 -0
- package/content/themes/casper/default.hbs +5 -5
- package/content/themes/casper/error-404.hbs +1 -1
- package/content/themes/casper/error.hbs +2 -2
- package/content/themes/casper/gulpfile.js +13 -2
- package/content/themes/casper/locales/context.json +112 -0
- package/content/themes/casper/locales/de-CH.json +112 -0
- package/content/themes/casper/locales/de.json +112 -0
- package/content/themes/casper/locales/en.json +112 -0
- package/content/themes/casper/locales/fr.json +112 -0
- package/content/themes/casper/locales/ga.json +112 -0
- package/content/themes/casper/locales/gd.json +112 -0
- package/content/themes/casper/locales/nl.json +112 -0
- package/content/themes/casper/locales/pt-BR.json +112 -0
- package/content/themes/casper/locales/sv.json +112 -0
- package/content/themes/casper/locales/uk.json +112 -0
- package/content/themes/casper/locales/zh.json +112 -0
- package/content/themes/casper/package.json +12 -11
- package/content/themes/casper/partials/post-card.hbs +4 -4
- package/content/themes/casper/post.hbs +4 -4
- package/content/themes/casper/tag.hbs +1 -1
- package/content/themes/source/LICENSE +1 -1
- package/content/themes/source/README.md +4 -1
- package/content/themes/source/assets/built/screen.css +1 -2
- package/content/themes/source/assets/built/screen.css.map +1 -1
- package/content/themes/source/assets/built/source.js +1 -2
- package/content/themes/source/assets/built/source.js.map +1 -1
- package/content/themes/source/assets/css/screen.css +18 -16
- package/content/themes/source/default.hbs +1 -1
- package/content/themes/source/gulpfile.js +5 -3
- package/content/themes/source/locales/context.json +112 -0
- package/content/themes/source/locales/de-CH.json +112 -0
- package/content/themes/source/locales/de.json +112 -0
- package/content/themes/source/locales/en.json +112 -0
- package/content/themes/source/locales/fr.json +112 -0
- package/content/themes/source/locales/ga.json +112 -0
- package/content/themes/source/locales/gd.json +112 -0
- package/content/themes/source/locales/nl.json +112 -0
- package/content/themes/source/locales/pt-BR.json +112 -0
- package/content/themes/source/locales/sv.json +112 -0
- package/content/themes/source/locales/uk.json +112 -0
- package/content/themes/source/locales/zh.json +112 -0
- package/content/themes/source/package.json +13 -11
- package/content/themes/source/partials/components/featured.hbs +1 -1
- package/content/themes/source/partials/components/footer.hbs +1 -1
- package/content/themes/source/partials/components/header-content.hbs +1 -1
- package/content/themes/source/partials/components/navigation.hbs +5 -5
- package/content/themes/source/partials/components/post-list.hbs +7 -7
- package/content/themes/source/partials/email-subscription.hbs +5 -5
- package/content/themes/source/partials/lightbox.hbs +6 -6
- package/content/themes/source/partials/post-card.hbs +1 -1
- package/content/themes/source/partials/search-toggle.hbs +1 -1
- package/content/themes/source/post.hbs +2 -2
- package/core/built/admin/assets/{PolarAngleAxis-a31TecIQ.js → PolarAngleAxis-D8t9UwHk.js} +1 -1
- package/core/built/admin/assets/{_baseAssignValue-Dw5pcaUJ.js → _baseAssignValue-DPyNU5o6.js} +1 -1
- package/core/built/admin/assets/{a-large-small-Cf_hUupE.js → a-large-small-BijOKCoa.js} +1 -1
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +1 -1
- package/core/built/admin/assets/admin-x-settings/{code-editor-view-C1ZFtR6M.mjs → code-editor-view-DJsXOcZ2.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-zyVbQw03.mjs → index-BnhIi5NU.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-D96qFjP9.mjs → index-CnwJhGbs.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-ILVAmH8Q.mjs → index-DaGaokMn.mjs} +1067 -1063
- package/core/built/admin/assets/admin-x-settings/{modals-BbDVa22r.mjs → modals-D02VJnTV.mjs} +2838 -2840
- package/core/built/admin/assets/{at-sign-DyMz5le1.js → at-sign-_JfhlimD.js} +1 -1
- package/core/built/admin/assets/{audience-BevM-Udw.js → audience-BdrhpNZ4.js} +1 -1
- package/core/built/admin/assets/{avatar-flipboard-DiGYhH_s.js → avatar-flipboard-BdmM8eOn.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-Dj4VOEDi.js → bluesky-sharing-yeM8KkY8.js} +1 -1
- package/core/built/admin/assets/{chart-BDdnkPgC.js → chart-CULaDoVB.js} +1 -1
- package/core/built/admin/assets/{chunk.138.5c4b0d0ae3f21c9c726d.js → chunk.482.c45c97fe666cd1a6b060.js} +4 -4
- package/core/built/admin/assets/{chunk.524.54ba5b50c191627c46f2.js → chunk.524.7fc2692374515780e66e.js} +4 -4
- package/core/built/admin/assets/{chunk.582.3232d44081d84d135e3d.js → chunk.582.fd8c4fa8e637e2d5611e.js} +8 -8
- package/core/built/admin/assets/{code-editor-view-V0wBeYuE.js → code-editor-view-UA2xKjk1.js} +1 -1
- package/core/built/admin/assets/{comments-CqurDRGu.js → comments-DoNjlMYA.js} +1 -1
- package/core/built/admin/assets/{content-helpers-C8lVEp2H.js → content-helpers-BRzxAgjP.js} +1 -1
- package/core/built/admin/assets/{copy-BdseBMKx.js → copy-CbX-_mHj.js} +1 -1
- package/core/built/admin/assets/{data-list--5Xfh5gm.js → data-list-nZOiCWV9.js} +1 -1
- package/core/built/admin/assets/{deleted-feed-item-Bmxp6PAR.js → deleted-feed-item-DjkZpKDC.js} +1 -1
- package/core/built/admin/assets/{edit-profile-Bi-G8vrq.js → edit-profile-BY_oJrQn.js} +1 -1
- package/core/built/admin/assets/{empty-indicator-C1cLHSRX.js → empty-indicator-DoxAratw.js} +1 -1
- package/core/built/admin/assets/{en-DPpVO1wK.js → en-B9ScC5j9.js} +1 -1
- package/core/built/admin/assets/{feed-lCu_EJ0a.js → feed-BsgqABLQ.js} +1 -1
- package/core/built/admin/assets/{filters-BYYV0fmK.js → filters-CscMb_Gl.js} +1 -1
- package/core/built/admin/assets/{gh-chart-BYWaeEuY.js → gh-chart-GokHrG5_.js} +1 -1
- package/core/built/admin/assets/{ghost-565670d46ad5b30935426b5dfa95bc81.js → ghost-d1843f84a9476ccbdf2a350848ac88aa.js} +2 -2
- package/core/built/admin/assets/{growth-DXHnB1EQ.js → growth-CKOWA1_V.js} +1 -1
- package/core/built/admin/assets/{hash-tZATAzZa.js → hash-twG7-iIB.js} +1 -1
- package/core/built/admin/assets/{inbox-B-LuWyHY.js → inbox-BdkIJbSq.js} +1 -1
- package/core/built/admin/assets/{index-BDrJ3N3y.js → index-B7OzWwwP.js} +1 -1
- package/core/built/admin/assets/{index-B8tGZsrG.js → index-BFySh9qo.js} +1 -1
- package/core/built/admin/assets/{index-EiGE3Aiy.js → index-BOWUajrT.js} +1 -1
- package/core/built/admin/assets/{index-ig4J4RpO.js → index-BQLCImHQ.js} +1 -1
- package/core/built/admin/assets/{index-BmKPrbBK.js → index-C398bG84.js} +1 -1
- package/core/built/admin/assets/{index-BX0_v5NB.js → index-C5YCjM6z.js} +1 -1
- package/core/built/admin/assets/{index-DEwhVcR5.js → index-CHpxzPRR.js} +1 -1
- package/core/built/admin/assets/{index-CX1A5TwO.js → index-CM-iA6b-.js} +3 -3
- package/core/built/admin/assets/{index-9bj69FKq.js → index-CgCHCdle.js} +1 -1
- package/core/built/admin/assets/{index-BocVu6WC.js → index-Cw441AFq.js} +1 -1
- package/core/built/admin/assets/{index-jHIJgh6L.js → index-D9S88kRl.js} +1 -1
- package/core/built/admin/assets/{index-CqvBC0jL.js → index-DbgA2vK8.js} +1 -1
- package/core/built/admin/assets/{index-2H-Jp89Z.js → index-ghPdV1ln.js} +1 -1
- package/core/built/admin/assets/{index-BcOzgmTC.js → index-tX0pajfP.js} +1 -1
- package/core/built/admin/assets/{index-BxVUX0dN.js → index-upXLfQxx.js} +1 -1
- package/core/built/admin/assets/{koenig-lexical-BdX9U-oB.js → koenig-lexical-GZS7bHDz.js} +1 -1
- package/core/built/admin/assets/{kpi-card-DQ1AbLJB.js → kpi-card-B6fmS7IH.js} +1 -1
- package/core/built/admin/assets/{kpis-vcbQvnLh.js → kpis-B-JXQ-4C.js} +1 -1
- package/core/built/admin/assets/{label-CNmRJkLy.js → label-ChHoKmj0.js} +1 -1
- package/core/built/admin/assets/{links-BE51SJxR.js → links-DQSOljcR.js} +1 -1
- package/core/built/admin/assets/{list-filter-BwnQ0Xlp.js → list-filter-cWuCDOLb.js} +1 -1
- package/core/built/admin/assets/{loader-circle-CcLEGvsN.js → loader-circle-B5ILzHHu.js} +1 -1
- package/core/built/admin/assets/{lucide-react-DHFgHS1l.js → lucide-react-CM0rB7CS.js} +1 -1
- package/core/built/admin/assets/{main-layout-DsxBNVo-.js → main-layout-BE7vZmy0.js} +1 -1
- package/core/built/admin/assets/{members-3okEkvWv.js → members-C10Z_BuG.js} +1 -1
- package/core/built/admin/assets/{message-square-text-BkERj4f5.js → message-square-text-DVZjnBBF.js} +1 -1
- package/core/built/admin/assets/{minus-C6oMPJo2.js → minus-BWrVDnd-.js} +1 -1
- package/core/built/admin/assets/{modals-CfUec0CL.js → modals-Bx7uDUHI.js} +15 -15
- package/core/built/admin/assets/{moderation-DEHUqi1e.js → moderation-DwlKZoB5.js} +1 -1
- package/core/built/admin/assets/{newsletter-Cp-39ksw.js → newsletter-DkOl42UB.js} +1 -1
- package/core/built/admin/assets/{newsletters-Bj58VQa2.js → newsletters-DNt9qd4h.js} +1 -1
- package/core/built/admin/assets/{note-9ysQWJz6.js → note-C-BLqULD.js} +1 -1
- package/core/built/admin/assets/{offers-DORdeAfE.js → offers-CU0KFuyW.js} +1 -1
- package/core/built/admin/assets/{overview-aZmI_71o.js → overview-DAEelSKq.js} +1 -1
- package/core/built/admin/assets/{pagemenu-DK1tbdsq.js → pagemenu-Cr7_YYR8.js} +1 -1
- package/core/built/admin/assets/{post-analytics-context-D8nk51yA.js → post-analytics-context-B2ytyOU0.js} +1 -1
- package/core/built/admin/assets/{post-analytics-header-qh1LEasb.js → post-analytics-header-CvR25xxU.js} +1 -1
- package/core/built/admin/assets/{post-analytics-HdZI-1J7.js → post-analytics-qpfoP-57.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-CvTf3PP9.js → post-share-modal-DDhiG0X4.js} +1 -1
- package/core/built/admin/assets/{posts-mIxckqHa.js → posts-DTy4lguT.js} +1 -1
- package/core/built/admin/assets/{referrers-qhD64pmp.js → referrers-BzKz9fT9.js} +1 -1
- package/core/built/admin/assets/{repeat-Idtsb62O.js → repeat-C_xWSLK1.js} +1 -1
- package/core/built/admin/assets/{reply-DXS-C-Rk.js → reply-CXnb6Juu.js} +1 -1
- package/core/built/admin/assets/{select-CeD8IhXy.js → select-WZUCQgjI.js} +1 -1
- package/core/built/admin/assets/{settings-BHfStwmS.js → settings-BC82mM6N.js} +1 -1
- package/core/built/admin/assets/{settings-M0QTt_tT.js → settings-EbntXZw9.js} +7 -7
- package/core/built/admin/assets/{sort-button-BoTFsBYE.js → sort-button-CFYf_NOQ.js} +1 -1
- package/core/built/admin/assets/{source-icon-opy8g_Nk.js → source-icon-xS8eeWX-.js} +1 -1
- package/core/built/admin/assets/{sprout-BKOFkdV8.js → sprout-CLmu2pln.js} +1 -1
- package/core/built/admin/assets/{square-BjZk7vJx.js → square-D0Y5-h0C.js} +1 -1
- package/core/built/admin/assets/{stats-DXXxmfRV.js → stats-B5O9DjYi.js} +1 -1
- package/core/built/admin/assets/{stats-view-D_5J0Nf7.js → stats-view-Cr6M1ycv.js} +1 -1
- package/core/built/admin/assets/{step-1-BcaR_MDC.js → step-1-DGbl6AAj.js} +1 -1
- package/core/built/admin/assets/{step-2-BR0jqEO_.js → step-2-Rf8oissi.js} +1 -1
- package/core/built/admin/assets/{step-3-CMPME0ky.js → step-3-tMPmbeBc.js} +1 -1
- package/core/built/admin/assets/{table-CwbnKMYe.js → table-CXo8NKU2.js} +1 -1
- package/core/built/admin/assets/{tabs-hfT89L_O.js → tabs-BMk71Tf2.js} +1 -1
- package/core/built/admin/assets/{tags-BSwYlj_e.js → tags-CSl0BjsA.js} +1 -1
- package/core/built/admin/assets/{tags-C3b5AHTm.js → tags-CqHx8i6Q.js} +1 -1
- package/core/built/admin/assets/{ticket-BnxPoED1.js → ticket-BtYGyQvH.js} +1 -1
- package/core/built/admin/assets/{tiers-DHKKzaBK.js → tiers-CPwRaPSt.js} +1 -1
- package/core/built/admin/assets/{toggle-group-U-Oqm2AA.js → toggle-group-QFge4qQs.js} +1 -1
- package/core/built/admin/assets/{topic-filter-28veMlW4.js → topic-filter-DnYtZ5e5.js} +1 -1
- package/core/built/admin/assets/{trash-D6TuYuIs.js → trash-B4ncKmRu.js} +1 -1
- package/core/built/admin/assets/{use-growth-stats-COmCRCzY.js → use-growth-stats-eFO-f1o1.js} +1 -1
- package/core/built/admin/assets/{use-infinite-virtual-scroll-mIEgtLoq.js → use-infinite-virtual-scroll-DTqnBtHM.js} +1 -1
- package/core/built/admin/assets/{use-scroll-restoration-CXVseKDj.js → use-scroll-restoration-o1DrSdlb.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-5-LKveLR.js → use-simple-pagination-BsBGNcxc.js} +1 -1
- package/core/built/admin/assets/{user-plus-CKQ-d0d4.js → user-plus-BS8SGBmk.js} +1 -1
- package/core/built/admin/assets/{user-round-check-BWc7Da4S.js → user-round-check-BgOPeBFs.js} +1 -1
- package/core/built/admin/assets/{wallet-cards-BlDtHc6_.js → wallet-cards-Be7G77lR.js} +1 -1
- package/core/built/admin/assets/{web-Bvvtl44p.js → web-BNs01IAt.js} +1 -1
- package/core/built/admin/index.html +5 -5
- package/core/frontend/helpers/cancel_link.js +2 -2
- package/core/server/api/endpoints/authors-public.js +3 -3
- package/core/server/api/endpoints/files.js +2 -1
- package/core/server/api/endpoints/pages-public.js +3 -3
- package/core/server/api/endpoints/posts-public.js +3 -3
- package/core/server/api/endpoints/users.js +6 -2
- package/core/server/api/endpoints/utils/api-filter-utils.js +22 -0
- package/core/server/api/endpoints/utils/api-filter-utils.ts +22 -0
- package/core/server/lib/lexical.js +2 -1
- package/core/server/lib/mobiledoc.js +1 -1
- package/core/server/lib/request-external.js +78 -2
- package/core/server/services/email-service/email-templates/partials/styles.hbs +3 -1
- package/core/server/services/koenig/node-renderers/image-renderer.js +80 -10
- package/core/server/services/koenig/render-utils/srcset-attribute.js +15 -3
- package/core/server/services/members/members-api/controllers/router-controller.js +5 -5
- package/core/server/services/members/members-api/repositories/member-repository.js +1 -1
- package/core/server/services/members/members-api/utils/get-discount-window.js +70 -4
- package/core/server/services/members/members-api/utils/has-active-offer.js +10 -14
- package/core/server/services/oembed/oembed-service.js +9 -1
- package/core/server/services/offers/application/offer-mapper.js +48 -0
- package/core/server/services/offers/application/offers-api.js +2 -2
- package/core/shared/config/defaults.json +1 -1
- package/core/shared/labs.js +2 -1
- package/package.json +6 -6
- package/tsconfig.tsbuildinfo +1 -1
- package/yarn.lock +148 -16
- package/components/tryghost-i18n-6.21.0.tgz +0 -0
- package/core/server/api/endpoints/utils/public-endpoint-utils.js +0 -18
- package/core/server/api/endpoints/utils/public-endpoint-utils.ts +0 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as t,aW as be,aV as xe,bK as Se,bL as je,B as oe,bM as _e,bN as Ce,bO as Ne,bP as ve,ck as Le,ao as ye,an as Te,aY as Fe,r as l,cl as Ee,aX as Z,aO as Oe,ak as ne,al as $,cm as ke,bS as De,aT as Me,u as Ie,aZ as Ae,aU as Pe}from"./index-CX1A5TwO.js";import{S as we,a as Ue,b as Ve,c as Re,d as Be,e as ze,f as Ke}from"./select-CeD8IhXy.js";import{u as W,S as ie,a as re,A as J,b as z,c as X,U as le}from"./post-analytics-context-D8nk51yA.js";import{C as $e,F as Ge}from"./loader-circle-CcLEGvsN.js";import{g as We,a as He,S as Qe,b as Ye}from"./kpis-vcbQvnLh.js";import{F as Xe,c as H,e as ce}from"./en-DPpVO1wK.js";import{C as ue,a as qe,b as Ze,h as Je,c as de,i as et,u as K,j as ee}from"./pagemenu-DK1tbdsq.js";import{D as tt,h as st,i as te,a as at,b as ot,c as nt,d as it,e as rt,f as lt,g as ct}from"./data-list--5Xfh5gm.js";import{a as ut}from"./wallet-cards-BlDtHc6_.js";import{P as dt,a as mt}from"./post-analytics-header-qh1LEasb.js";import{F as pt,c as ht}from"./filters-BYYV0fmK.js";import{b as gt,c as ft,F as bt,S as xt,T as St}from"./lucide-react-DHFgHS1l.js";import{U as jt}from"./user-plus-CKQ-d0d4.js";import{M as _t}from"./ticket-BnxPoED1.js";import{F as Ct}from"./message-square-text-BkERj4f5.js";import{E as Nt}from"./empty-indicator-C1cLHSRX.js";import"./posts-mIxckqHa.js";import"./source-icon-opy8g_Nk.js";import"./source-utils-B1S3ZHA2.js";import"./gh-chart-BYWaeEuY.js";import"./chart-BDdnkPgC.js";import"./_baseAssignValue-Dw5pcaUJ.js";import"./tabs-hfT89L_O.js";import"./index-BX0_v5NB.js";import"./post-share-modal-CvTf3PP9.js";import"./get-site-timezone-BDBAcjmq.js";import"./post-helpers-gInwAwEv.js";import"./trash-D6TuYuIs.js";import"./sprout-BKOFkdV8.js";import"./a-large-small-Cf_hUupE.js";import"./list-filter-BwnQ0Xlp.js";import"./at-sign-DyMz5le1.js";import"./copy-BdseBMKx.js";import"./hash-tZATAzZa.js";import"./inbox-B-LuWyHY.js";import"./minus-C6oMPJo2.js";import"./tags-C3b5AHTm.js";import"./square-BjZk7vJx.js";import"./user-round-check-BWc7Da4S.js";import"./repeat-Idtsb62O.js";import"./reply-DXS-C-Rk.js";const se=()=>{const{range:e,setRange:s}=W();return t.jsxs(we,{value:`${e}`,onValueChange:a=>{s(Number(a))},children:[t.jsxs(Ue,{className:"w-auto",children:[t.jsx($e,{className:"mr-2",size:16,strokeWidth:1.5}),t.jsx(Ve,{placeholder:"Select a period"})]}),t.jsx(Re,{align:"end",children:t.jsxs(Be,{children:[t.jsx(ze,{children:"Period"}),Object.values(ie).map(a=>t.jsx(Ke,{value:`${a.value}`,children:a.name},a.value))]})})]})};H.registerLocale(ce);const vt=e=>re[e]||H.getName(e,"en")||"Unknown",Lt=e=>{const s={"UNITED STATES":"US","UNITED STATES OF AMERICA":"US",USA:"US","UNITED KINGDOM":"GB",UK:"GB","GREAT BRITAIN":"GB",NETHERLANDS:"NL"},a=e.toUpperCase();return s[a]||(e.length>2?e.substring(0,2):e)},ae=({tableHeader:e,data:s,onLocationClick:a})=>t.jsxs(tt,{children:[e&&t.jsxs(st,{children:[t.jsx(te,{children:"Country"}),t.jsx(te,{children:"Visitors"})]}),t.jsx(at,{children:s.map(i=>{const o=vt(`${i.location}`),r=a&&i.location!=="Unknown",c=i.location?i.location.toLowerCase():"unknown";return t.jsxs(ot,{className:r?"cursor-pointer":"","data-testid":`location-row-${c}`,onClick:r?()=>a(i.location):void 0,children:[t.jsx(nt,{style:{width:`${i.percentage?Math.round(i.percentage*100):0}%`}}),t.jsx(it,{className:"group-hover/data:max-w-[calc(100%-140px)]",children:t.jsxs("div",{className:"flex items-center space-x-3 overflow-hidden",title:o,children:[t.jsx(Xe,{countryCode:`${Lt(i.location)}`,fallback:t.jsx("span",{className:"flex h-[14px] w-[22px] items-center justify-center rounded-[2px] bg-black text-white",children:t.jsx(Le.SkullAndBones,{className:"size-3"})})}),t.jsx("div",{className:"truncate font-medium",children:o})]})}),t.jsxs(rt,{children:[t.jsx(lt,{children:ye(Number(i.visits))}),t.jsx(ct,{children:Te(i.percentage)})]})]},i.location||"unknown")})})]}),yt=({data:e,isLoading:s,onLocationClick:a})=>{const i=e.slice(0,10);return t.jsx(t.Fragment,{children:s?"":t.jsx(t.Fragment,{children:e&&e.length>0&&t.jsxs(ue,{className:"group/datalist","data-testid":"locations-card",children:[t.jsxs("div",{className:"flex items-center justify-between p-6",children:[t.jsxs(qe,{className:"p-0",children:[t.jsx(Ze,{children:"Locations"}),t.jsx(Je,{children:"Where are the readers of this post"})]}),t.jsx(be,{className:"mr-2",children:"Visitors"})]}),t.jsxs(de,{className:"overflow-hidden",children:[t.jsx(xe,{}),t.jsx(ae,{data:i,tableHeader:!1,onLocationClick:a})]}),e.length>10&&t.jsx(et,{children:t.jsxs(Se,{children:[t.jsx(je,{asChild:!0,children:t.jsxs(oe,{variant:"outline",children:["View all ",t.jsx(ut,{})]})}),t.jsxs(_e,{className:"overflow-y-auto pt-0 sm:max-w-[600px]",children:[t.jsxs(Ce,{className:"sticky top-0 z-40 -mx-6 bg-background/60 p-6 backdrop-blur",children:[t.jsx(Ne,{children:"Top locations"}),t.jsx(ve,{children:"Where are the readers of this post"})]}),t.jsx("div",{className:"group/datalist",children:t.jsx(ae,{data:e,tableHeader:!0,onLocationClick:a})})]})]})})]})})})},me=e=>!e||e.length===0?J:z.filter(s=>e.includes(s.value)).reduce((s,a)=>s|a.bit,0)||J,pe=e=>{const s=[];return(e&X.PUBLIC)!==0&&s.push(z[0].value),(e&X.FREE)!==0&&s.push(z[1].value),(e&X.PAID)!==0&&s.push(z[2].value),s.join(",")};H.registerLocale(ce);const Tt=e=>re[e]||H.getName(e,"en")||e,Ft=({visits:e})=>t.jsx("span",{className:"order-2 font-mono text-xs text-muted-foreground",children:e.toLocaleString()}),Et={utm_source:{endpoint:"api_top_utm_sources",valueKey:"utm_source",transformValue:e=>({value:e||"(not set)",label:e||"(not set)"})},utm_medium:{endpoint:"api_top_utm_mediums",valueKey:"utm_medium",transformValue:e=>({value:e||"(not set)",label:e||"(not set)"})},utm_campaign:{endpoint:"api_top_utm_campaigns",valueKey:"utm_campaign",transformValue:e=>({value:e||"(not set)",label:e||"(not set)"})},utm_content:{endpoint:"api_top_utm_contents",valueKey:"utm_content",transformValue:e=>({value:e||"(not set)",label:e||"(not set)"})},utm_term:{endpoint:"api_top_utm_terms",valueKey:"utm_term",transformValue:e=>({value:e||"(not set)",label:e||"(not set)"})},source:{endpoint:"api_top_sources",valueKey:"source",transformValue:e=>({value:e||"",label:e||"Direct"})},location:{endpoint:"api_top_locations",valueKey:"location",filterItem(e){const s=String(e.location||"");return s!==""&&!le.includes(s)},transformValue:e=>({value:e,label:Tt(e)})},device:{endpoint:"api_top_devices",valueKey:"device",transformValue:e=>({value:e,label:e==="mobile-ios"?"iOS":e==="mobile-android"?"Android":e==="desktop"?"Desktop":e==="bot"?"Bot":e})}},Ot=(e,s,a)=>{const i={...a};return e.forEach(o=>{if(o.field===s||o.values.length===0)return;const r=o.values[0];o.field!=="audience"&&(o.field==="source"||o.field==="device"||o.field==="location"||o.field.startsWith("utm_"))&&(i[o.field]=r)}),i},O=(e,s=[],a,i={})=>{const{enabled:o=!0}=i,{statsConfig:r,range:c}=W(),{startDate:b,endDate:m,timezone:x}=ne(c),p=Et[e],d=l.useMemo(()=>{const j=s.find(h=>h.field==="audience");return me(j?.values)},[s]),S=l.useMemo(()=>{const j={site_uuid:r?.id||"",date_from:$(b),date_to:$(m),timezone:x,member_status:pe(d),limit:"50"};return a&&(j.post_uuid=a),Ot(s,e,j)},[r?.id,b,m,x,d,s,e,a]),{data:v,loading:k}=K({endpoint:p?.endpoint||"",statsConfig:r,params:S,enabled:o&&!!p});return{options:l.useMemo(()=>p?(v||[]).filter(h=>p.filterItem?p.filterItem(h):!0).map(h=>{const F=String(h[p.valueKey]??""),A=Number(h.visits)||0,{value:E,label:y}=p.transformValue?p.transformValue(F):{value:F,label:F};return{label:y,value:E,icon:t.jsx(Ft,{visits:A})}}):[],[v,p]),loading:k}};function kt({filters:e,onChange:s,...a}){const{appSettings:i}=Fe(),{post:o}=W(),r=o?.uuid,[c,b]=l.useState(null),[m,x]=l.useState(!1);l.useEffect(()=>{const g=window.matchMedia("(max-width: 1024px)"),C=T=>{x(T.matches)};return C(g),g.addEventListener("change",C),()=>g.removeEventListener("change",C)},[]);const p=l.useMemo(()=>{const g=[{value:"undefined",label:"Public visitors",icon:t.jsx(Z,{className:"text-gray-700"})},{value:"free",label:"Free members",icon:t.jsx(Oe,{className:"text-green"})},{value:"paid",label:"Paid members",icon:t.jsx(jt,{className:"text-orange"})}];return i?.paidMembersEnabled?g:g.filter(C=>C.value!=="paid")},[i?.paidMembersEnabled]),d=l.useCallback(g=>{const C=c===g,T=e.some(n=>n.field===g);return C||T},[c,e]),{options:S,loading:v}=O("utm_source",e,r,{enabled:d("utm_source")}),{options:k,loading:I}=O("utm_medium",e,r,{enabled:d("utm_medium")}),{options:j,loading:h}=O("utm_campaign",e,r,{enabled:d("utm_campaign")}),{options:F,loading:A}=O("utm_content",e,r,{enabled:d("utm_content")}),{options:E,loading:y}=O("utm_term",e,r,{enabled:d("utm_term")}),{options:R,loading:D}=O("source",e,r,{enabled:d("source")}),{options:U,loading:M}=O("device",e,r,{enabled:d("device")}),{options:B,loading:P}=O("location",e,r,{enabled:d("location")}),_=l.useMemo(()=>[{value:"is",label:"is"}],[]),Q=l.useMemo(()=>{const g=[{key:"utm_source",label:"UTM Source",type:"select",icon:t.jsx(_t,{className:"size-4"}),placeholder:"Select source",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:S,isLoading:v,searchable:!0,selectedOptionsClassName:"hidden"},{key:"utm_medium",label:"UTM Medium",type:"select",icon:t.jsx(xt,{className:"size-4"}),placeholder:"Select medium",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:k,isLoading:I,className:"w-60",popoverContentClassName:"w-60",searchable:!0,selectedOptionsClassName:"hidden"},{key:"utm_campaign",label:"UTM Campaign",type:"select",icon:t.jsx(Ct,{className:"size-4"}),placeholder:"Select campaign",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:j,isLoading:h,className:"w-60",popoverContentClassName:"w-60",searchable:!0,selectedOptionsClassName:"hidden"},{key:"utm_content",label:"UTM Content",type:"select",icon:t.jsx(St,{className:"size-4"}),placeholder:"Select content",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:F,isLoading:A,className:"w-60",popoverContentClassName:"w-60",searchable:!0,selectedOptionsClassName:"hidden"},{key:"utm_term",label:"UTM Term",type:"select",icon:t.jsx(ke,{className:"size-4"}),placeholder:"Select term",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:E,isLoading:y,className:"w-60",popoverContentClassName:"w-60",searchable:!0,selectedOptionsClassName:"hidden"}];return[{group:"Basic",fields:[{key:"audience",label:"Audience",type:"multiselect",icon:t.jsx(Ee,{}),options:p.map(({value:C,label:T,icon:n})=>({value:C,label:T,icon:n})),defaultOperator:"is any of",hideOperatorSelect:!0,autoCloseOnSelect:!0},{key:"source",label:"Source",type:"select",icon:t.jsx(Z,{className:"size-4"}),placeholder:"Select source",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:R,isLoading:D,className:"w-60",popoverContentClassName:"w-60",searchable:!0,selectedOptionsClassName:"hidden"},{key:"device",label:"Device",type:"select",icon:t.jsx(gt,{className:"size-4"}),placeholder:"Select device",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:U,isLoading:M,selectedOptionsClassName:"hidden"},{key:"location",label:"Location",type:"select",icon:t.jsx(ft,{className:"size-4"}),placeholder:"Select location",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:B,isLoading:P,searchable:!0,selectedOptionsClassName:"hidden"}]},{group:"UTM parameters",fields:g}]},[S,v,k,I,j,h,F,A,E,y,_,p,R,D,U,M,B,P]),w=e.length>0,Y=l.useCallback(()=>{s&&s([])},[s]);return t.jsxs("div",{className:"mt-3 flex w-full justify-between gap-2 lg:mt-0","data-testid":"stats-filter-container",children:[t.jsx(pt,{addButtonIcon:t.jsx(Ge,{}),addButtonText:w?"Add filter":"Filter",allowMultiple:!1,className:`[&>button]:order-last ${w&&"[&>button]:border-none"}`,fields:Q,filters:e,keyboardShortcut:"f",popoverAlign:m||w?"start":"end",showSearchInput:!1,onActiveFieldChange:b,onChange:s||(()=>{}),...a}),w&&t.jsxs(oe,{className:"hidden font-normal text-muted-foreground lg:flex","data-testid":"stats-filter-clear-button",variant:"ghost",onClick:Y,children:[t.jsx(bt,{}),"Clear"]})]})}const G=["audience","source","device","location","utm_source","utm_medium","utm_campaign","utm_content","utm_term"],he="__empty__",ge="%2C";function Dt(e){const s=new URLSearchParams;return e.forEach(a=>{if(G.includes(a.field)&&a.values.length>0){const i=a.values.map(o=>o===""?he:String(o).replace(/,/g,ge)).join(",");s.set(a.field,i)}}),s}const q=new Map;function Mt(e){return q.has(e)||q.set(e,`url-${e}-${Date.now()}-${Math.random().toString(36).substring(2,11)}`),q.get(e)}function It(e){const s=[],a=new Set(G);return e.forEach((i,o)=>{if(!a.has(o))return;const r=i.split(",").map(c=>c===he?"":c.replace(new RegExp(ge,"g"),","));if(r.length>0){const c=o==="audience"?"is any of":"is";s.push({id:Mt(o),field:o,operator:c,values:r})}}),s}function At(e={}){const[s,a]=De(),{onFiltersChange:i}=e,o=l.useRef(!1),r=l.useMemo(()=>It(s),[s]);l.useEffect(()=>{!o.current&&i&&i(r)},[r,i]);const c=l.useCallback(m=>{o.current=!0;const x=typeof m=="function"?m(r):m,p=Dt(x),d=new URLSearchParams(s);G.forEach(S=>{d.delete(S)}),p.forEach((S,v)=>{d.set(v,S)}),a(d,{replace:!0}),setTimeout(()=>{o.current=!1},0)},[r,s,a]),b=l.useCallback(()=>{o.current=!0;const m=new URLSearchParams(s);G.forEach(x=>{m.delete(x)}),a(m,{replace:!0}),setTimeout(()=>{o.current=!1},0)},[s,a]);return{filters:r,setFilters:c,clearFilters:b}}const Ns=()=>{const e=Me(),{postId:s}=Ie(),{statsConfig:a,isLoading:i,range:o,data:r,post:c,isPostLoading:b}=W(),{filters:m,setFilters:x}=At(),p=l.useMemo(()=>{const n=m.find(f=>f.field==="audience");return me(n?.values)},[m]);l.useEffect(()=>{!b&&c?.email_only&&e(`/posts/analytics/${s}`)},[b,c?.email_only,e,s]);const d=l.useMemo(()=>{if(!c?.published_at)return ie.ALL_TIME.value;const n=Ae(c.published_at);return o>n?n:o},[c?.published_at,o]),{startDate:S,endDate:v,timezone:k}=ne(d),I=l.useCallback(()=>{const n=document.querySelector(".overflow-y-scroll");n&&n.scrollTo({top:0,behavior:"smooth"})},[]),j=l.useMemo(()=>{const n={};return m.forEach(f=>{const N=f.field,L=f.values;if(N==="audience")return;const u=L&&L.length>0&&L[0]!==null&&L[0]!==void 0,V=N==="source"&&L?.[0]==="";if(u&&(L[0]!==""||V)){const fe=String(L[0]);n[N]=fe}}),n},[m]),h=l.useCallback((n,f)=>{x(N=>N.find(u=>u.field===n)?N.map(u=>u.field===n?{...u,values:[f]}:u):[...N,ht(n,"is",[f])]),I()},[x,I]),F=l.useCallback(n=>h("location",n),[h]),A=l.useCallback(n=>h("source",n),[h]),E=l.useMemo(()=>{const n={site_uuid:a?.id||"",date_from:$(S),date_to:$(v),timezone:k,member_status:pe(p),post_uuid:"",...j};return!b&&c?.uuid?{...n,post_uuid:c.uuid}:n},[b,c,a?.id,S,v,k,p,j]),{data:y,loading:R}=K({endpoint:"api_kpis",statsConfig:a||{id:""},params:E}),{data:D,loading:U}=K({endpoint:"api_top_locations",statsConfig:a||{id:""},params:E}),{data:M,loading:B}=K({endpoint:"api_top_sources",statsConfig:a||{id:""},params:E}),P=l.useMemo(()=>D?.reduce((n,f)=>n+Number(f.visits),0)||0,[D]),_=l.useMemo(()=>M?M.reduce((n,f)=>n+Number(f.visits||0),0):0,[M]),Q=r?.url,w=r?.icon,Y=l.useMemo(()=>{const n=D?.map(u=>({location:String(u.location),visits:Number(u.visits),percentage:P>0?Number(u.visits)/P:0,isUnknown:le.includes(String(u.location))}))||[],f=n.filter(u=>!u.isUnknown),N=n.filter(u=>u.isUnknown),L=N.length>0?[{location:"Unknown",visits:N.reduce((u,V)=>u+V.visits,0),percentage:N.reduce((u,V)=>u+V.percentage,0)}]:[];return[...f,...L]},[D,P]),g=i||b||R||U||B,C=We(y),T=m.length>0;return t.jsxs(t.Fragment,{children:[t.jsxs(dt,{currentTab:"Web",children:[T&&t.jsx(ee,{children:t.jsx(se,{})}),t.jsxs(ee,{className:`${T?"!mt-0 [grid-area:subactions] lg:!mt-[25px]":"[grid-area:actions]"}`,children:[t.jsx(kt,{filters:m,onChange:x}),!T&&t.jsx(se,{})]})]}),t.jsx(mt,{children:g?t.jsx(ue,{className:"size-full",variant:"plain",children:t.jsx(de,{className:"size-full items-center justify-center",children:t.jsx(Pe,{})})}):y&&y.length!==0&&C.visits!=="0"?t.jsxs(t.Fragment,{children:[t.jsx(He,{data:y,range:d}),t.jsxs("div",{className:"flex flex-col gap-6 lg:grid lg:grid-cols-2",children:[t.jsx(yt,{data:Y,isLoading:U,onLocationClick:F}),t.jsx(Qe,{data:M,range:d,siteIcon:w,siteUrl:Q,totalVisitors:_,onSourceClick:A})]})]}):t.jsx("div",{className:"grow",children:t.jsx(Nt,{className:"h-full",description:"Try adjusting filters to see more data.",title:`No visitors ${Ye(o)}`,children:t.jsx(Z,{strokeWidth:1.5})})})})]})};export{Ns as default};
|
|
1
|
+
import{j as t,aW as be,aV as xe,bK as Se,bL as je,B as oe,bM as _e,bN as Ce,bO as Ne,bP as ve,ck as Le,ao as ye,an as Te,aY as Fe,r as l,cl as Ee,aX as Z,aO as Oe,ak as ne,al as $,cm as ke,bS as De,aT as Me,u as Ie,aZ as Ae,aU as Pe}from"./index-CM-iA6b-.js";import{S as we,a as Ue,b as Ve,c as Re,d as Be,e as ze,f as Ke}from"./select-WZUCQgjI.js";import{u as W,S as ie,a as re,A as J,b as z,c as X,U as le}from"./post-analytics-context-B2ytyOU0.js";import{C as $e,F as Ge}from"./loader-circle-B5ILzHHu.js";import{g as We,a as He,S as Qe,b as Ye}from"./kpis-B-JXQ-4C.js";import{F as Xe,c as H,e as ce}from"./en-B9ScC5j9.js";import{C as ue,a as qe,b as Ze,h as Je,c as de,i as et,u as K,j as ee}from"./pagemenu-Cr7_YYR8.js";import{D as tt,h as st,i as te,a as at,b as ot,c as nt,d as it,e as rt,f as lt,g as ct}from"./data-list-nZOiCWV9.js";import{a as ut}from"./wallet-cards-Be7G77lR.js";import{P as dt,a as mt}from"./post-analytics-header-CvR25xxU.js";import{F as pt,c as ht}from"./filters-CscMb_Gl.js";import{b as gt,c as ft,F as bt,S as xt,T as St}from"./lucide-react-CM0rB7CS.js";import{U as jt}from"./user-plus-BS8SGBmk.js";import{M as _t}from"./ticket-BtYGyQvH.js";import{F as Ct}from"./message-square-text-DVZjnBBF.js";import{E as Nt}from"./empty-indicator-DoxAratw.js";import"./posts-DTy4lguT.js";import"./source-icon-xS8eeWX-.js";import"./source-utils-B1S3ZHA2.js";import"./gh-chart-GokHrG5_.js";import"./chart-CULaDoVB.js";import"./_baseAssignValue-DPyNU5o6.js";import"./tabs-BMk71Tf2.js";import"./index-C5YCjM6z.js";import"./post-share-modal-DDhiG0X4.js";import"./get-site-timezone-BDBAcjmq.js";import"./post-helpers-gInwAwEv.js";import"./trash-B4ncKmRu.js";import"./sprout-CLmu2pln.js";import"./a-large-small-BijOKCoa.js";import"./list-filter-cWuCDOLb.js";import"./at-sign-_JfhlimD.js";import"./copy-CbX-_mHj.js";import"./hash-twG7-iIB.js";import"./inbox-BdkIJbSq.js";import"./minus-BWrVDnd-.js";import"./tags-CqHx8i6Q.js";import"./square-D0Y5-h0C.js";import"./user-round-check-BgOPeBFs.js";import"./repeat-C_xWSLK1.js";import"./reply-CXnb6Juu.js";const se=()=>{const{range:e,setRange:s}=W();return t.jsxs(we,{value:`${e}`,onValueChange:a=>{s(Number(a))},children:[t.jsxs(Ue,{className:"w-auto",children:[t.jsx($e,{className:"mr-2",size:16,strokeWidth:1.5}),t.jsx(Ve,{placeholder:"Select a period"})]}),t.jsx(Re,{align:"end",children:t.jsxs(Be,{children:[t.jsx(ze,{children:"Period"}),Object.values(ie).map(a=>t.jsx(Ke,{value:`${a.value}`,children:a.name},a.value))]})})]})};H.registerLocale(ce);const vt=e=>re[e]||H.getName(e,"en")||"Unknown",Lt=e=>{const s={"UNITED STATES":"US","UNITED STATES OF AMERICA":"US",USA:"US","UNITED KINGDOM":"GB",UK:"GB","GREAT BRITAIN":"GB",NETHERLANDS:"NL"},a=e.toUpperCase();return s[a]||(e.length>2?e.substring(0,2):e)},ae=({tableHeader:e,data:s,onLocationClick:a})=>t.jsxs(tt,{children:[e&&t.jsxs(st,{children:[t.jsx(te,{children:"Country"}),t.jsx(te,{children:"Visitors"})]}),t.jsx(at,{children:s.map(i=>{const o=vt(`${i.location}`),r=a&&i.location!=="Unknown",c=i.location?i.location.toLowerCase():"unknown";return t.jsxs(ot,{className:r?"cursor-pointer":"","data-testid":`location-row-${c}`,onClick:r?()=>a(i.location):void 0,children:[t.jsx(nt,{style:{width:`${i.percentage?Math.round(i.percentage*100):0}%`}}),t.jsx(it,{className:"group-hover/data:max-w-[calc(100%-140px)]",children:t.jsxs("div",{className:"flex items-center space-x-3 overflow-hidden",title:o,children:[t.jsx(Xe,{countryCode:`${Lt(i.location)}`,fallback:t.jsx("span",{className:"flex h-[14px] w-[22px] items-center justify-center rounded-[2px] bg-black text-white",children:t.jsx(Le.SkullAndBones,{className:"size-3"})})}),t.jsx("div",{className:"truncate font-medium",children:o})]})}),t.jsxs(rt,{children:[t.jsx(lt,{children:ye(Number(i.visits))}),t.jsx(ct,{children:Te(i.percentage)})]})]},i.location||"unknown")})})]}),yt=({data:e,isLoading:s,onLocationClick:a})=>{const i=e.slice(0,10);return t.jsx(t.Fragment,{children:s?"":t.jsx(t.Fragment,{children:e&&e.length>0&&t.jsxs(ue,{className:"group/datalist","data-testid":"locations-card",children:[t.jsxs("div",{className:"flex items-center justify-between p-6",children:[t.jsxs(qe,{className:"p-0",children:[t.jsx(Ze,{children:"Locations"}),t.jsx(Je,{children:"Where are the readers of this post"})]}),t.jsx(be,{className:"mr-2",children:"Visitors"})]}),t.jsxs(de,{className:"overflow-hidden",children:[t.jsx(xe,{}),t.jsx(ae,{data:i,tableHeader:!1,onLocationClick:a})]}),e.length>10&&t.jsx(et,{children:t.jsxs(Se,{children:[t.jsx(je,{asChild:!0,children:t.jsxs(oe,{variant:"outline",children:["View all ",t.jsx(ut,{})]})}),t.jsxs(_e,{className:"overflow-y-auto pt-0 sm:max-w-[600px]",children:[t.jsxs(Ce,{className:"sticky top-0 z-40 -mx-6 bg-background/60 p-6 backdrop-blur",children:[t.jsx(Ne,{children:"Top locations"}),t.jsx(ve,{children:"Where are the readers of this post"})]}),t.jsx("div",{className:"group/datalist",children:t.jsx(ae,{data:e,tableHeader:!0,onLocationClick:a})})]})]})})]})})})},me=e=>!e||e.length===0?J:z.filter(s=>e.includes(s.value)).reduce((s,a)=>s|a.bit,0)||J,pe=e=>{const s=[];return(e&X.PUBLIC)!==0&&s.push(z[0].value),(e&X.FREE)!==0&&s.push(z[1].value),(e&X.PAID)!==0&&s.push(z[2].value),s.join(",")};H.registerLocale(ce);const Tt=e=>re[e]||H.getName(e,"en")||e,Ft=({visits:e})=>t.jsx("span",{className:"order-2 font-mono text-xs text-muted-foreground",children:e.toLocaleString()}),Et={utm_source:{endpoint:"api_top_utm_sources",valueKey:"utm_source",transformValue:e=>({value:e||"(not set)",label:e||"(not set)"})},utm_medium:{endpoint:"api_top_utm_mediums",valueKey:"utm_medium",transformValue:e=>({value:e||"(not set)",label:e||"(not set)"})},utm_campaign:{endpoint:"api_top_utm_campaigns",valueKey:"utm_campaign",transformValue:e=>({value:e||"(not set)",label:e||"(not set)"})},utm_content:{endpoint:"api_top_utm_contents",valueKey:"utm_content",transformValue:e=>({value:e||"(not set)",label:e||"(not set)"})},utm_term:{endpoint:"api_top_utm_terms",valueKey:"utm_term",transformValue:e=>({value:e||"(not set)",label:e||"(not set)"})},source:{endpoint:"api_top_sources",valueKey:"source",transformValue:e=>({value:e||"",label:e||"Direct"})},location:{endpoint:"api_top_locations",valueKey:"location",filterItem(e){const s=String(e.location||"");return s!==""&&!le.includes(s)},transformValue:e=>({value:e,label:Tt(e)})},device:{endpoint:"api_top_devices",valueKey:"device",transformValue:e=>({value:e,label:e==="mobile-ios"?"iOS":e==="mobile-android"?"Android":e==="desktop"?"Desktop":e==="bot"?"Bot":e})}},Ot=(e,s,a)=>{const i={...a};return e.forEach(o=>{if(o.field===s||o.values.length===0)return;const r=o.values[0];o.field!=="audience"&&(o.field==="source"||o.field==="device"||o.field==="location"||o.field.startsWith("utm_"))&&(i[o.field]=r)}),i},O=(e,s=[],a,i={})=>{const{enabled:o=!0}=i,{statsConfig:r,range:c}=W(),{startDate:b,endDate:m,timezone:x}=ne(c),p=Et[e],d=l.useMemo(()=>{const j=s.find(h=>h.field==="audience");return me(j?.values)},[s]),S=l.useMemo(()=>{const j={site_uuid:r?.id||"",date_from:$(b),date_to:$(m),timezone:x,member_status:pe(d),limit:"50"};return a&&(j.post_uuid=a),Ot(s,e,j)},[r?.id,b,m,x,d,s,e,a]),{data:v,loading:k}=K({endpoint:p?.endpoint||"",statsConfig:r,params:S,enabled:o&&!!p});return{options:l.useMemo(()=>p?(v||[]).filter(h=>p.filterItem?p.filterItem(h):!0).map(h=>{const F=String(h[p.valueKey]??""),A=Number(h.visits)||0,{value:E,label:y}=p.transformValue?p.transformValue(F):{value:F,label:F};return{label:y,value:E,icon:t.jsx(Ft,{visits:A})}}):[],[v,p]),loading:k}};function kt({filters:e,onChange:s,...a}){const{appSettings:i}=Fe(),{post:o}=W(),r=o?.uuid,[c,b]=l.useState(null),[m,x]=l.useState(!1);l.useEffect(()=>{const g=window.matchMedia("(max-width: 1024px)"),C=T=>{x(T.matches)};return C(g),g.addEventListener("change",C),()=>g.removeEventListener("change",C)},[]);const p=l.useMemo(()=>{const g=[{value:"undefined",label:"Public visitors",icon:t.jsx(Z,{className:"text-gray-700"})},{value:"free",label:"Free members",icon:t.jsx(Oe,{className:"text-green"})},{value:"paid",label:"Paid members",icon:t.jsx(jt,{className:"text-orange"})}];return i?.paidMembersEnabled?g:g.filter(C=>C.value!=="paid")},[i?.paidMembersEnabled]),d=l.useCallback(g=>{const C=c===g,T=e.some(n=>n.field===g);return C||T},[c,e]),{options:S,loading:v}=O("utm_source",e,r,{enabled:d("utm_source")}),{options:k,loading:I}=O("utm_medium",e,r,{enabled:d("utm_medium")}),{options:j,loading:h}=O("utm_campaign",e,r,{enabled:d("utm_campaign")}),{options:F,loading:A}=O("utm_content",e,r,{enabled:d("utm_content")}),{options:E,loading:y}=O("utm_term",e,r,{enabled:d("utm_term")}),{options:R,loading:D}=O("source",e,r,{enabled:d("source")}),{options:U,loading:M}=O("device",e,r,{enabled:d("device")}),{options:B,loading:P}=O("location",e,r,{enabled:d("location")}),_=l.useMemo(()=>[{value:"is",label:"is"}],[]),Q=l.useMemo(()=>{const g=[{key:"utm_source",label:"UTM Source",type:"select",icon:t.jsx(_t,{className:"size-4"}),placeholder:"Select source",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:S,isLoading:v,searchable:!0,selectedOptionsClassName:"hidden"},{key:"utm_medium",label:"UTM Medium",type:"select",icon:t.jsx(xt,{className:"size-4"}),placeholder:"Select medium",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:k,isLoading:I,className:"w-60",popoverContentClassName:"w-60",searchable:!0,selectedOptionsClassName:"hidden"},{key:"utm_campaign",label:"UTM Campaign",type:"select",icon:t.jsx(Ct,{className:"size-4"}),placeholder:"Select campaign",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:j,isLoading:h,className:"w-60",popoverContentClassName:"w-60",searchable:!0,selectedOptionsClassName:"hidden"},{key:"utm_content",label:"UTM Content",type:"select",icon:t.jsx(St,{className:"size-4"}),placeholder:"Select content",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:F,isLoading:A,className:"w-60",popoverContentClassName:"w-60",searchable:!0,selectedOptionsClassName:"hidden"},{key:"utm_term",label:"UTM Term",type:"select",icon:t.jsx(ke,{className:"size-4"}),placeholder:"Select term",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:E,isLoading:y,className:"w-60",popoverContentClassName:"w-60",searchable:!0,selectedOptionsClassName:"hidden"}];return[{group:"Basic",fields:[{key:"audience",label:"Audience",type:"multiselect",icon:t.jsx(Ee,{}),options:p.map(({value:C,label:T,icon:n})=>({value:C,label:T,icon:n})),defaultOperator:"is any of",hideOperatorSelect:!0,autoCloseOnSelect:!0},{key:"source",label:"Source",type:"select",icon:t.jsx(Z,{className:"size-4"}),placeholder:"Select source",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:R,isLoading:D,className:"w-60",popoverContentClassName:"w-60",searchable:!0,selectedOptionsClassName:"hidden"},{key:"device",label:"Device",type:"select",icon:t.jsx(gt,{className:"size-4"}),placeholder:"Select device",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:U,isLoading:M,selectedOptionsClassName:"hidden"},{key:"location",label:"Location",type:"select",icon:t.jsx(ft,{className:"size-4"}),placeholder:"Select location",operators:_,defaultOperator:"is",hideOperatorSelect:!0,options:B,isLoading:P,searchable:!0,selectedOptionsClassName:"hidden"}]},{group:"UTM parameters",fields:g}]},[S,v,k,I,j,h,F,A,E,y,_,p,R,D,U,M,B,P]),w=e.length>0,Y=l.useCallback(()=>{s&&s([])},[s]);return t.jsxs("div",{className:"mt-3 flex w-full justify-between gap-2 lg:mt-0","data-testid":"stats-filter-container",children:[t.jsx(pt,{addButtonIcon:t.jsx(Ge,{}),addButtonText:w?"Add filter":"Filter",allowMultiple:!1,className:`[&>button]:order-last ${w&&"[&>button]:border-none"}`,fields:Q,filters:e,keyboardShortcut:"f",popoverAlign:m||w?"start":"end",showSearchInput:!1,onActiveFieldChange:b,onChange:s||(()=>{}),...a}),w&&t.jsxs(oe,{className:"hidden font-normal text-muted-foreground lg:flex","data-testid":"stats-filter-clear-button",variant:"ghost",onClick:Y,children:[t.jsx(bt,{}),"Clear"]})]})}const G=["audience","source","device","location","utm_source","utm_medium","utm_campaign","utm_content","utm_term"],he="__empty__",ge="%2C";function Dt(e){const s=new URLSearchParams;return e.forEach(a=>{if(G.includes(a.field)&&a.values.length>0){const i=a.values.map(o=>o===""?he:String(o).replace(/,/g,ge)).join(",");s.set(a.field,i)}}),s}const q=new Map;function Mt(e){return q.has(e)||q.set(e,`url-${e}-${Date.now()}-${Math.random().toString(36).substring(2,11)}`),q.get(e)}function It(e){const s=[],a=new Set(G);return e.forEach((i,o)=>{if(!a.has(o))return;const r=i.split(",").map(c=>c===he?"":c.replace(new RegExp(ge,"g"),","));if(r.length>0){const c=o==="audience"?"is any of":"is";s.push({id:Mt(o),field:o,operator:c,values:r})}}),s}function At(e={}){const[s,a]=De(),{onFiltersChange:i}=e,o=l.useRef(!1),r=l.useMemo(()=>It(s),[s]);l.useEffect(()=>{!o.current&&i&&i(r)},[r,i]);const c=l.useCallback(m=>{o.current=!0;const x=typeof m=="function"?m(r):m,p=Dt(x),d=new URLSearchParams(s);G.forEach(S=>{d.delete(S)}),p.forEach((S,v)=>{d.set(v,S)}),a(d,{replace:!0}),setTimeout(()=>{o.current=!1},0)},[r,s,a]),b=l.useCallback(()=>{o.current=!0;const m=new URLSearchParams(s);G.forEach(x=>{m.delete(x)}),a(m,{replace:!0}),setTimeout(()=>{o.current=!1},0)},[s,a]);return{filters:r,setFilters:c,clearFilters:b}}const Ns=()=>{const e=Me(),{postId:s}=Ie(),{statsConfig:a,isLoading:i,range:o,data:r,post:c,isPostLoading:b}=W(),{filters:m,setFilters:x}=At(),p=l.useMemo(()=>{const n=m.find(f=>f.field==="audience");return me(n?.values)},[m]);l.useEffect(()=>{!b&&c?.email_only&&e(`/posts/analytics/${s}`)},[b,c?.email_only,e,s]);const d=l.useMemo(()=>{if(!c?.published_at)return ie.ALL_TIME.value;const n=Ae(c.published_at);return o>n?n:o},[c?.published_at,o]),{startDate:S,endDate:v,timezone:k}=ne(d),I=l.useCallback(()=>{const n=document.querySelector(".overflow-y-scroll");n&&n.scrollTo({top:0,behavior:"smooth"})},[]),j=l.useMemo(()=>{const n={};return m.forEach(f=>{const N=f.field,L=f.values;if(N==="audience")return;const u=L&&L.length>0&&L[0]!==null&&L[0]!==void 0,V=N==="source"&&L?.[0]==="";if(u&&(L[0]!==""||V)){const fe=String(L[0]);n[N]=fe}}),n},[m]),h=l.useCallback((n,f)=>{x(N=>N.find(u=>u.field===n)?N.map(u=>u.field===n?{...u,values:[f]}:u):[...N,ht(n,"is",[f])]),I()},[x,I]),F=l.useCallback(n=>h("location",n),[h]),A=l.useCallback(n=>h("source",n),[h]),E=l.useMemo(()=>{const n={site_uuid:a?.id||"",date_from:$(S),date_to:$(v),timezone:k,member_status:pe(p),post_uuid:"",...j};return!b&&c?.uuid?{...n,post_uuid:c.uuid}:n},[b,c,a?.id,S,v,k,p,j]),{data:y,loading:R}=K({endpoint:"api_kpis",statsConfig:a||{id:""},params:E}),{data:D,loading:U}=K({endpoint:"api_top_locations",statsConfig:a||{id:""},params:E}),{data:M,loading:B}=K({endpoint:"api_top_sources",statsConfig:a||{id:""},params:E}),P=l.useMemo(()=>D?.reduce((n,f)=>n+Number(f.visits),0)||0,[D]),_=l.useMemo(()=>M?M.reduce((n,f)=>n+Number(f.visits||0),0):0,[M]),Q=r?.url,w=r?.icon,Y=l.useMemo(()=>{const n=D?.map(u=>({location:String(u.location),visits:Number(u.visits),percentage:P>0?Number(u.visits)/P:0,isUnknown:le.includes(String(u.location))}))||[],f=n.filter(u=>!u.isUnknown),N=n.filter(u=>u.isUnknown),L=N.length>0?[{location:"Unknown",visits:N.reduce((u,V)=>u+V.visits,0),percentage:N.reduce((u,V)=>u+V.percentage,0)}]:[];return[...f,...L]},[D,P]),g=i||b||R||U||B,C=We(y),T=m.length>0;return t.jsxs(t.Fragment,{children:[t.jsxs(dt,{currentTab:"Web",children:[T&&t.jsx(ee,{children:t.jsx(se,{})}),t.jsxs(ee,{className:`${T?"!mt-0 [grid-area:subactions] lg:!mt-[25px]":"[grid-area:actions]"}`,children:[t.jsx(kt,{filters:m,onChange:x}),!T&&t.jsx(se,{})]})]}),t.jsx(mt,{children:g?t.jsx(ue,{className:"size-full",variant:"plain",children:t.jsx(de,{className:"size-full items-center justify-center",children:t.jsx(Pe,{})})}):y&&y.length!==0&&C.visits!=="0"?t.jsxs(t.Fragment,{children:[t.jsx(He,{data:y,range:d}),t.jsxs("div",{className:"flex flex-col gap-6 lg:grid lg:grid-cols-2",children:[t.jsx(yt,{data:Y,isLoading:U,onLocationClick:F}),t.jsx(Qe,{data:M,range:d,siteIcon:w,siteUrl:Q,totalVisitors:_,onSourceClick:A})]})]}):t.jsx("div",{className:"grow",children:t.jsx(Nt,{className:"h-full",description:"Try adjusting filters to see more data.",title:`No visitors ${Ye(o)}`,children:t.jsx(Z,{strokeWidth:1.5})})})})]})};export{Ns as default};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<link rel="stylesheet" href="./assets/vendor-0ede59da8efb5e28fa929557f7ff7154.css">
|
|
5
5
|
<link rel="stylesheet" href="./assets/ghost-1627c2071621864e79a845529769ff19.css">
|
|
6
|
-
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%226.21%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%228785c87985%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%
|
|
6
|
+
<meta name="ghost-admin/config/environment" content="%7B%22modulePrefix%22%3A%22ghost-admin%22%2C%22environment%22%3A%22production%22%2C%22editorUrl%22%3A%22%22%2C%22rootURL%22%3A%22%22%2C%22locationType%22%3A%22trailing-hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%2C%22Array%22%3Atrue%2C%22String%22%3Atrue%2C%22Function%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_JQUERY_INTEGRATION%22%3Atrue%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22version%22%3A%226.21%22%2C%22name%22%3A%22ghost-admin%22%7D%2C%22ember-simple-auth%22%3A%7B%7D%2C%22%40sentry%2Fember%22%3A%7B%22disablePerformance%22%3Atrue%2C%22sentry%22%3A%7B%7D%7D%2C%22ember-cli-mirage%22%3A%7B%22usingProxy%22%3Afalse%2C%22useDefaultPassthroughs%22%3Atrue%7D%2C%22exportApplicationGlobal%22%3Afalse%2C%22ember-load%22%3A%7B%22loadingIndicatorClass%22%3A%22ember-load-indicator%22%7D%2C%22editorFilename%22%3A%22koenig-lexical.umd.js%22%2C%22editorHash%22%3A%228785c87985%22%2C%22adminXSettingsFilename%22%3A%22admin-x-settings.js%22%2C%22adminXSettingsHash%22%3A%2224fb0ad2c1%22%2C%22activitypubFilename%22%3A%22activitypub.js%22%2C%22activitypubHash%22%3A%2258788e91cd%22%2C%22postsFilename%22%3A%22posts.js%22%2C%22postsHash%22%3A%22b8baca4053%22%2C%22statsFilename%22%3A%22stats.js%22%2C%22statsHash%22%3A%22a37abdfe6f%22%2C%22activitypubRemoteConfigUrl%22%3A%22%2F.ghost%2Factivitypub%2Fstable%2Fclient-config%22%7D">
|
|
7
7
|
|
|
8
8
|
<meta charset="UTF-8" />
|
|
9
9
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<meta name="apple-mobile-web-app-title" content="Ghost" />
|
|
18
18
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
19
19
|
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
|
20
|
-
<script type="module" crossorigin src="./assets/index-
|
|
20
|
+
<script type="module" crossorigin src="./assets/index-CM-iA6b-.js"></script>
|
|
21
21
|
<link rel="stylesheet" crossorigin href="./assets/index-BQQ-gECj.css">
|
|
22
22
|
</head>
|
|
23
23
|
<body class="react-admin">
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
<div id="ember-notifications-wormhole"></div>
|
|
40
40
|
<script src="./assets/vendor-0e6161f8da2ad46cf62cef0a53cfb16a.js"></script>
|
|
41
41
|
<script src="./assets/chunk.119.8ed8af850063f930a797.js"></script>
|
|
42
|
-
<script src="./assets/chunk.
|
|
43
|
-
<script src="./assets/chunk.524.
|
|
44
|
-
<script src="./assets/ghost-
|
|
42
|
+
<script src="./assets/chunk.482.c45c97fe666cd1a6b060.js"></script>
|
|
43
|
+
<script src="./assets/chunk.524.7fc2692374515780e66e.js"></script>
|
|
44
|
+
<script src="./assets/ghost-d1843f84a9476ccbdf2a350848ac88aa.js"></script>
|
|
45
45
|
</body>
|
|
46
46
|
</html>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// Should be used inside of a subscription context, e.g.: `{{#foreach @member.subscriptions}} {{cancel_link}} {{/foreach}}`
|
|
5
5
|
// Outputs cancel/renew links to manage subscription renewal after the subscription period ends.
|
|
6
6
|
//
|
|
7
|
-
// Defaults to class="cancel-subscription-link" errorClass="cancel-subscription-error" cancelLabel="Cancel subscription" continueLabel="
|
|
7
|
+
// Defaults to class="cancel-subscription-link" errorClass="cancel-subscription-error" cancelLabel="Cancel subscription" continueLabel="Resume subscription"
|
|
8
8
|
const {labs} = require('../services/proxy');
|
|
9
9
|
const {templates} = require('../services/handlebars');
|
|
10
10
|
|
|
@@ -28,7 +28,7 @@ function cancel_link(options) { // eslint-disable-line camelcase
|
|
|
28
28
|
class: truncateOptions.class || 'gh-subscription-cancel',
|
|
29
29
|
errorClass: truncateOptions.errorClass || 'gh-error gh-error-subscription-cancel',
|
|
30
30
|
cancelLabel: truncateOptions.cancelLabel || 'Cancel subscription',
|
|
31
|
-
continueLabel: truncateOptions.continueLabel || '
|
|
31
|
+
continueLabel: truncateOptions.continueLabel || 'Resume subscription'
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
return templates.execute('cancel_link', data);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const tpl = require('@tryghost/tpl');
|
|
2
2
|
const errors = require('@tryghost/errors');
|
|
3
3
|
const models = require('../../models');
|
|
4
|
-
const {
|
|
4
|
+
const {rejectContentApiRestrictedFieldsTransformer} = require('./utils/api-filter-utils');
|
|
5
5
|
|
|
6
6
|
const ALLOWED_INCLUDES = ['count.posts'];
|
|
7
7
|
|
|
@@ -36,7 +36,7 @@ const controller = {
|
|
|
36
36
|
query(frame) {
|
|
37
37
|
const options = {
|
|
38
38
|
...frame.options,
|
|
39
|
-
mongoTransformer:
|
|
39
|
+
mongoTransformer: rejectContentApiRestrictedFieldsTransformer
|
|
40
40
|
};
|
|
41
41
|
return models.Author.findPage(options);
|
|
42
42
|
}
|
|
@@ -68,7 +68,7 @@ const controller = {
|
|
|
68
68
|
async query(frame) {
|
|
69
69
|
const options = {
|
|
70
70
|
...frame.options,
|
|
71
|
-
mongoTransformer:
|
|
71
|
+
mongoTransformer: rejectContentApiRestrictedFieldsTransformer
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
const model = await models.Author.findOne(frame.data, options);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const storage = require('../../adapters/storage');
|
|
2
|
+
const mime = require('mime-types');
|
|
2
3
|
|
|
3
4
|
/** @type {import('@tryghost/api-framework').Controller} */
|
|
4
5
|
const controller = {
|
|
@@ -13,7 +14,7 @@ const controller = {
|
|
|
13
14
|
const filePath = await storage.getStorage('files').save({
|
|
14
15
|
name: frame.file.originalname,
|
|
15
16
|
path: frame.file.path,
|
|
16
|
-
type: frame.file.
|
|
17
|
+
type: mime.lookup(frame.file.originalname) || 'application/octet-stream'
|
|
17
18
|
});
|
|
18
19
|
|
|
19
20
|
return {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const tpl = require('@tryghost/tpl');
|
|
2
2
|
const errors = require('@tryghost/errors');
|
|
3
3
|
const models = require('../../models');
|
|
4
|
-
const {
|
|
4
|
+
const {rejectContentApiRestrictedFieldsTransformer} = require('./utils/api-filter-utils');
|
|
5
5
|
|
|
6
6
|
const ALLOWED_INCLUDES = ['tags', 'authors', 'tiers'];
|
|
7
7
|
|
|
@@ -42,7 +42,7 @@ const controller = {
|
|
|
42
42
|
query(frame) {
|
|
43
43
|
const options = {
|
|
44
44
|
...frame.options,
|
|
45
|
-
mongoTransformer:
|
|
45
|
+
mongoTransformer: rejectContentApiRestrictedFieldsTransformer
|
|
46
46
|
};
|
|
47
47
|
return models.Post.findPage(options);
|
|
48
48
|
}
|
|
@@ -78,7 +78,7 @@ const controller = {
|
|
|
78
78
|
async query(frame) {
|
|
79
79
|
const options = {
|
|
80
80
|
...frame.options,
|
|
81
|
-
mongoTransformer:
|
|
81
|
+
mongoTransformer: rejectContentApiRestrictedFieldsTransformer
|
|
82
82
|
};
|
|
83
83
|
const model = await models.Post.findOne(frame.data, options);
|
|
84
84
|
if (!model) {
|
|
@@ -4,7 +4,7 @@ const errors = require('@tryghost/errors');
|
|
|
4
4
|
const postsPublicService = require('../../services/posts-public');
|
|
5
5
|
const getPostServiceInstance = require('../../services/posts/posts-service-instance');
|
|
6
6
|
const postsService = getPostServiceInstance();
|
|
7
|
-
const {
|
|
7
|
+
const {rejectContentApiRestrictedFieldsTransformer} = require('./utils/api-filter-utils');
|
|
8
8
|
|
|
9
9
|
const ALLOWED_INCLUDES = ['tags', 'authors', 'tiers', 'sentiment'];
|
|
10
10
|
|
|
@@ -100,7 +100,7 @@ const controller = {
|
|
|
100
100
|
query(frame) {
|
|
101
101
|
const options = {
|
|
102
102
|
...frame.options,
|
|
103
|
-
mongoTransformer:
|
|
103
|
+
mongoTransformer: rejectContentApiRestrictedFieldsTransformer
|
|
104
104
|
};
|
|
105
105
|
return postsService.browsePosts(options);
|
|
106
106
|
}
|
|
@@ -154,7 +154,7 @@ const controller = {
|
|
|
154
154
|
async query(frame) {
|
|
155
155
|
const options = {
|
|
156
156
|
...frame.options,
|
|
157
|
-
mongoTransformer:
|
|
157
|
+
mongoTransformer: rejectContentApiRestrictedFieldsTransformer
|
|
158
158
|
};
|
|
159
159
|
const model = await models.Post.findOne(frame.data, options);
|
|
160
160
|
if (!model) {
|
|
@@ -6,6 +6,7 @@ const dbBackup = require('../../data/db/backup');
|
|
|
6
6
|
const auth = require('../../services/auth');
|
|
7
7
|
const apiMail = require('./index').mail;
|
|
8
8
|
const apiSettings = require('./index').settings;
|
|
9
|
+
const {rejectAdminApiRestrictedFieldsTransformer} = require('./utils/api-filter-utils');
|
|
9
10
|
const UsersService = require('../../services/users');
|
|
10
11
|
const userService = new UsersService({dbBackup, models, auth, apiMail, apiSettings});
|
|
11
12
|
const ALLOWED_INCLUDES = ['count.posts', 'permissions', 'roles', 'roles.permissions'];
|
|
@@ -106,7 +107,11 @@ const controller = {
|
|
|
106
107
|
},
|
|
107
108
|
permissions: true,
|
|
108
109
|
query(frame) {
|
|
109
|
-
|
|
110
|
+
const options = {
|
|
111
|
+
...frame.options,
|
|
112
|
+
mongoTransformer: rejectAdminApiRestrictedFieldsTransformer
|
|
113
|
+
};
|
|
114
|
+
return models.User.findPage(options);
|
|
110
115
|
}
|
|
111
116
|
},
|
|
112
117
|
|
|
@@ -116,7 +121,6 @@ const controller = {
|
|
|
116
121
|
},
|
|
117
122
|
options: [
|
|
118
123
|
'include',
|
|
119
|
-
'filter',
|
|
120
124
|
'fields',
|
|
121
125
|
'debug'
|
|
122
126
|
],
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rejectAdminApiRestrictedFieldsTransformer = exports.rejectContentApiRestrictedFieldsTransformer = void 0;
|
|
4
|
+
const mongo_utils_1 = require("@tryghost/mongo-utils");
|
|
5
|
+
const CONTENT_API_RESTRICTED_FIELDS = new Set([
|
|
6
|
+
'password',
|
|
7
|
+
'email'
|
|
8
|
+
]);
|
|
9
|
+
const ADMIN_API_RESTRICTED_FIELDS = new Set([
|
|
10
|
+
'password'
|
|
11
|
+
]);
|
|
12
|
+
function hasRestrictedSegment(key, fields) {
|
|
13
|
+
return key.toLowerCase().split('.').some(segment => fields.has(segment));
|
|
14
|
+
}
|
|
15
|
+
const rejectContentApiRestrictedFieldsTransformer = (input) => {
|
|
16
|
+
return (0, mongo_utils_1.rejectStatements)(input, (key) => hasRestrictedSegment(key, CONTENT_API_RESTRICTED_FIELDS));
|
|
17
|
+
};
|
|
18
|
+
exports.rejectContentApiRestrictedFieldsTransformer = rejectContentApiRestrictedFieldsTransformer;
|
|
19
|
+
const rejectAdminApiRestrictedFieldsTransformer = (input) => {
|
|
20
|
+
return (0, mongo_utils_1.rejectStatements)(input, (key) => hasRestrictedSegment(key, ADMIN_API_RESTRICTED_FIELDS));
|
|
21
|
+
};
|
|
22
|
+
exports.rejectAdminApiRestrictedFieldsTransformer = rejectAdminApiRestrictedFieldsTransformer;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {rejectStatements} from '@tryghost/mongo-utils';
|
|
2
|
+
|
|
3
|
+
const CONTENT_API_RESTRICTED_FIELDS = new Set([
|
|
4
|
+
'password',
|
|
5
|
+
'email'
|
|
6
|
+
]);
|
|
7
|
+
|
|
8
|
+
const ADMIN_API_RESTRICTED_FIELDS = new Set([
|
|
9
|
+
'password'
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
function hasRestrictedSegment(key: string, fields: Set<string>): boolean {
|
|
13
|
+
return key.toLowerCase().split('.').some(segment => fields.has(segment));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const rejectContentApiRestrictedFieldsTransformer = (input: unknown) => {
|
|
17
|
+
return rejectStatements(input, (key: string) => hasRestrictedSegment(key, CONTENT_API_RESTRICTED_FIELDS));
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const rejectAdminApiRestrictedFieldsTransformer = (input: unknown) => {
|
|
21
|
+
return rejectStatements(input, (key: string) => hasRestrictedSegment(key, ADMIN_API_RESTRICTED_FIELDS));
|
|
22
|
+
};
|
|
@@ -97,7 +97,8 @@ module.exports = {
|
|
|
97
97
|
feature: {
|
|
98
98
|
contentVisibility: true, // force on until Koenig has been bumped
|
|
99
99
|
emailCustomization: true, // force on until Koenig has been bumped
|
|
100
|
-
emailUniqueid: labs.isSet('emailUniqueid')
|
|
100
|
+
emailUniqueid: labs.isSet('emailUniqueid'),
|
|
101
|
+
pictureImageFormats: labs.isSet('pictureImageFormats')
|
|
101
102
|
},
|
|
102
103
|
nodeRenderers: this.customNodeRenderers
|
|
103
104
|
}, userOptions);
|
|
@@ -140,7 +140,7 @@ module.exports = {
|
|
|
140
140
|
size = await getUnsplashSize(payload.src);
|
|
141
141
|
} else if (isRelativeImagePath || storageUtils.isLocalImage(payload.src)) {
|
|
142
142
|
size = await imageSize.getOriginalImageSizeFromStorageUrl(payload.src);
|
|
143
|
-
} else {
|
|
143
|
+
} else if (storageUtils.isInternalImage(payload.src)) {
|
|
144
144
|
size = await imageSize.getImageSizeFromUrl(payload.src);
|
|
145
145
|
}
|
|
146
146
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const got = /** @type {Got} */ (/** @type {unknown} */ (require('got')));
|
|
7
|
+
const dns = require('dns');
|
|
7
8
|
const net = require('net');
|
|
8
9
|
const dnsPromises = require('dns').promises;
|
|
9
10
|
const errors = require('@tryghost/errors');
|
|
@@ -139,6 +140,23 @@ function isPrivateIp(addr) {
|
|
|
139
140
|
if (/^fe[89ab][0-9a-f]:/i.test(normalized6)) {
|
|
140
141
|
return true;
|
|
141
142
|
}
|
|
143
|
+
// Re-check for IPv4-mapped IPv6 after normalization
|
|
144
|
+
// Handles expanded forms like 0:0:0:0:0:ffff:127.0.0.1 which normalize to ::ffff:...
|
|
145
|
+
const v4DottedNorm = normalized6.match(/^::ffff:(\d[\d.]+)$/i);
|
|
146
|
+
if (v4DottedNorm) {
|
|
147
|
+
const normV4 = normalizeIPv4(v4DottedNorm[1]);
|
|
148
|
+
if (normV4) {
|
|
149
|
+
return isPrivateIPv4(normV4);
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
const v4HexNorm = normalized6.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);
|
|
154
|
+
if (v4HexNorm) {
|
|
155
|
+
const hi = parseInt(v4HexNorm[1], 16);
|
|
156
|
+
const lo = parseInt(v4HexNorm[2], 16);
|
|
157
|
+
const mapped = ((hi >> 8) & 0xff) + '.' + (hi & 0xff) + '.' + ((lo >> 8) & 0xff) + '.' + (lo & 0xff);
|
|
158
|
+
return isPrivateIPv4(mapped);
|
|
159
|
+
}
|
|
142
160
|
return false;
|
|
143
161
|
}
|
|
144
162
|
|
|
@@ -193,8 +211,65 @@ async function disableRetries(options) {
|
|
|
193
211
|
options.timeout = 5000;
|
|
194
212
|
}
|
|
195
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Install a custom dnsLookup on the request options that validates the resolved
|
|
216
|
+
* IP at connection time. This eliminates the DNS rebinding / TOCTOU gap between
|
|
217
|
+
* the beforeRequest DNS check and the actual TCP connection: the IP validated
|
|
218
|
+
* here is the same one Node's http module will connect to.
|
|
219
|
+
*/
|
|
220
|
+
function installSafeDnsLookup(options) {
|
|
221
|
+
if (config.get('env') === 'development') {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const siteUrl = new URL(config.get('url'));
|
|
226
|
+
if (options.url.host === siteUrl.host) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const requestHref = options.url.href;
|
|
231
|
+
// Use 'lookup' (the native http.request option) rather than 'dnsLookup'
|
|
232
|
+
// (got's public API property which doesn't flow to the native request).
|
|
233
|
+
options.lookup = (hostname, dnsOpts, callback) => {
|
|
234
|
+
if (typeof dnsOpts === 'function') {
|
|
235
|
+
callback = dnsOpts;
|
|
236
|
+
dnsOpts = {};
|
|
237
|
+
}
|
|
238
|
+
dns.lookup(hostname, dnsOpts, (err, addressOrResult, family) => {
|
|
239
|
+
if (err) {
|
|
240
|
+
return callback(err, addressOrResult, family);
|
|
241
|
+
}
|
|
242
|
+
// When all:true, result is an array of {address, family} objects
|
|
243
|
+
if (dnsOpts && dnsOpts.all) {
|
|
244
|
+
const results = /** @type {{address: string, family: number}[]} */ (addressOrResult);
|
|
245
|
+
for (const entry of results) {
|
|
246
|
+
if (isPrivateIp(entry.address)) {
|
|
247
|
+
return callback(new errors.InternalServerError({
|
|
248
|
+
message: 'URL resolves to a non-permitted private IP block',
|
|
249
|
+
code: 'URL_PRIVATE_INVALID',
|
|
250
|
+
context: requestHref
|
|
251
|
+
}));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return callback(null, results);
|
|
255
|
+
}
|
|
256
|
+
if (isPrivateIp(/** @type {string} */ (addressOrResult))) {
|
|
257
|
+
return callback(new errors.InternalServerError({
|
|
258
|
+
message: 'URL resolves to a non-permitted private IP block',
|
|
259
|
+
code: 'URL_PRIVATE_INVALID',
|
|
260
|
+
context: requestHref
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
263
|
+
callback(null, addressOrResult, family);
|
|
264
|
+
});
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
196
268
|
// same as our normal request lib but if any request in a redirect chain resolves
|
|
197
269
|
// to a private IP address it will be blocked before the request is made.
|
|
270
|
+
// The beforeRequest hooks provide a first-pass DNS check with clear error messages.
|
|
271
|
+
// installSafeDnsLookup provides the authoritative gate at the connection layer,
|
|
272
|
+
// preventing DNS rebinding attacks where the IP changes between check and connect.
|
|
198
273
|
/** @type {ExtendOptions} */
|
|
199
274
|
const gotOpts = {
|
|
200
275
|
headers: {
|
|
@@ -203,11 +278,12 @@ const gotOpts = {
|
|
|
203
278
|
timeout: 10000, // default is no timeout
|
|
204
279
|
hooks: {
|
|
205
280
|
init: process.env.NODE_ENV?.startsWith('test') ? [disableRetries] : [],
|
|
206
|
-
beforeRequest: [errorIfInvalidUrl, errorIfHostnameResolvesToPrivateIp],
|
|
207
|
-
beforeRedirect: [errorIfHostnameResolvesToPrivateIp]
|
|
281
|
+
beforeRequest: [errorIfInvalidUrl, errorIfHostnameResolvesToPrivateIp, installSafeDnsLookup],
|
|
282
|
+
beforeRedirect: [errorIfHostnameResolvesToPrivateIp, installSafeDnsLookup]
|
|
208
283
|
}
|
|
209
284
|
};
|
|
210
285
|
|
|
211
286
|
const externalRequest = got.extend(gotOpts);
|
|
212
287
|
externalRequest.isPrivateIp = isPrivateIp;
|
|
288
|
+
externalRequest._installSafeDnsLookup = installSafeDnsLookup;
|
|
213
289
|
module.exports = externalRequest;
|
|
@@ -1048,7 +1048,8 @@ td.kg-card-spacing {
|
|
|
1048
1048
|
}
|
|
1049
1049
|
|
|
1050
1050
|
.kg-bookmark-thumbnail {
|
|
1051
|
-
|
|
1051
|
+
flex: 0 1 33%;
|
|
1052
|
+
width: 33%;
|
|
1052
1053
|
max-width: 180px;
|
|
1053
1054
|
background-repeat: no-repeat;
|
|
1054
1055
|
background-size: cover;
|
|
@@ -1082,6 +1083,7 @@ td.kg-card-spacing {
|
|
|
1082
1083
|
|
|
1083
1084
|
.kg-bookmark-author {
|
|
1084
1085
|
line-height: 1.5em;
|
|
1086
|
+
white-space: nowrap;
|
|
1085
1087
|
}
|
|
1086
1088
|
|
|
1087
1089
|
.kg-bookmark-publisher {
|
|
@@ -1,10 +1,24 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const imageTransform = require('@tryghost/image-transform');
|
|
1
3
|
const {getAvailableImageWidths} = require('../render-utils/get-available-image-widths');
|
|
2
4
|
const {isContentImage} = require('../render-utils/is-content-image');
|
|
3
|
-
const {setSrcsetAttribute} = require('../render-utils/srcset-attribute');
|
|
5
|
+
const {getSrcsetAttribute, setSrcsetAttribute} = require('../render-utils/srcset-attribute');
|
|
4
6
|
const {getResizedImageDimensions} = require('../render-utils/get-resized-image-dimensions');
|
|
5
7
|
const {addCreateDocumentOption} = require('../render-utils/add-create-document-option');
|
|
6
8
|
const {renderEmptyContainer} = require('../render-utils/render-empty-container');
|
|
7
9
|
|
|
10
|
+
const MODERN_IMAGE_FORMATS = ['avif', 'webp'];
|
|
11
|
+
|
|
12
|
+
function isAnimatedImage(url = '') {
|
|
13
|
+
try {
|
|
14
|
+
const parsedUrl = new URL(url, 'http://localhost');
|
|
15
|
+
const extension = path.extname(parsedUrl.pathname).toLowerCase();
|
|
16
|
+
return extension === '.gif';
|
|
17
|
+
} catch (err) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
8
22
|
function renderImageNode(node, options = {}) {
|
|
9
23
|
addCreateDocumentOption(options);
|
|
10
24
|
|
|
@@ -62,22 +76,78 @@ function renderImageNode(node, options = {}) {
|
|
|
62
76
|
img.setAttribute('height', height);
|
|
63
77
|
}
|
|
64
78
|
|
|
79
|
+
const imgAttributes = {
|
|
80
|
+
src: node.src,
|
|
81
|
+
width: node.width,
|
|
82
|
+
height: node.height
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
let picture = null;
|
|
86
|
+
|
|
65
87
|
if (options.target !== 'email') {
|
|
66
|
-
const imgAttributes = {
|
|
67
|
-
src: node.src,
|
|
68
|
-
width: node.width,
|
|
69
|
-
height: node.height
|
|
70
|
-
};
|
|
71
88
|
setSrcsetAttribute(img, imgAttributes, options);
|
|
72
89
|
|
|
90
|
+
let sizes;
|
|
73
91
|
if (img.getAttribute('srcset') && node.width && node.width >= 720) {
|
|
74
92
|
// standard size
|
|
75
93
|
if (!node.cardWidth || node.cardWidth === 'regular') {
|
|
76
|
-
|
|
94
|
+
sizes = '(min-width: 720px) 720px';
|
|
77
95
|
}
|
|
78
96
|
|
|
79
97
|
if (node.cardWidth === 'wide' && node.width >= 1200) {
|
|
80
|
-
|
|
98
|
+
sizes = '(min-width: 1200px) 1200px';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (sizes) {
|
|
103
|
+
img.setAttribute('sizes', sizes);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const shouldRenderPicture = Boolean(
|
|
107
|
+
options.feature?.pictureImageFormats &&
|
|
108
|
+
img.getAttribute('srcset') &&
|
|
109
|
+
!isAnimatedImage(node.src) &&
|
|
110
|
+
isContentImage(node.src, options.siteUrl, options.imageBaseUrl) &&
|
|
111
|
+
options.canTransformImage?.(node.src) &&
|
|
112
|
+
imageTransform.canTransformFiles()
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (shouldRenderPicture) {
|
|
116
|
+
picture = document.createElement('picture');
|
|
117
|
+
let sourcesAdded = false;
|
|
118
|
+
|
|
119
|
+
MODERN_IMAGE_FORMATS.forEach((format) => {
|
|
120
|
+
if (!imageTransform.canTransformToFormat(format)) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const formattedSrcset = getSrcsetAttribute({
|
|
125
|
+
src: node.src,
|
|
126
|
+
width: node.width,
|
|
127
|
+
options,
|
|
128
|
+
format
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (!formattedSrcset) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const source = document.createElement('source');
|
|
136
|
+
source.setAttribute('srcset', formattedSrcset);
|
|
137
|
+
source.setAttribute('type', `image/${format}`);
|
|
138
|
+
|
|
139
|
+
if (sizes) {
|
|
140
|
+
source.setAttribute('sizes', sizes);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
picture.appendChild(source);
|
|
144
|
+
sourcesAdded = true;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (sourcesAdded) {
|
|
148
|
+
picture.appendChild(img);
|
|
149
|
+
} else {
|
|
150
|
+
picture = null;
|
|
81
151
|
}
|
|
82
152
|
}
|
|
83
153
|
}
|
|
@@ -113,10 +183,10 @@ function renderImageNode(node, options = {}) {
|
|
|
113
183
|
if (node.href) {
|
|
114
184
|
const a = document.createElement('a');
|
|
115
185
|
a.setAttribute('href', node.href);
|
|
116
|
-
a.appendChild(img);
|
|
186
|
+
a.appendChild(picture || img);
|
|
117
187
|
figure.appendChild(a);
|
|
118
188
|
} else {
|
|
119
|
-
figure.appendChild(img);
|
|
189
|
+
figure.appendChild(picture || img);
|
|
120
190
|
}
|
|
121
191
|
|
|
122
192
|
if (node.caption) {
|
|
@@ -4,7 +4,7 @@ const {isUnsplashImage} = require('./is-unsplash-image');
|
|
|
4
4
|
|
|
5
5
|
// default content sizes: [600, 1000, 1600, 2400]
|
|
6
6
|
|
|
7
|
-
const getSrcsetAttribute = function ({src, width, options}) {
|
|
7
|
+
const getSrcsetAttribute = function ({src, width, options, format}) {
|
|
8
8
|
if (!options.imageOptimization || options.imageOptimization.srcsets === false || !width || !options.imageOptimization.contentImageSizes) {
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
@@ -23,10 +23,19 @@ const getSrcsetAttribute = function ({src, width, options}) {
|
|
|
23
23
|
srcsetWidths.forEach((srcsetWidth) => {
|
|
24
24
|
if (srcsetWidth === width) {
|
|
25
25
|
// use original image path if width matches exactly (avoids 302s from size->original)
|
|
26
|
-
|
|
26
|
+
// unless a specific output format was requested
|
|
27
|
+
if (format) {
|
|
28
|
+
srcs.push(`${imagesPath}/size/w${srcsetWidth}/format/${format}/${filename} ${srcsetWidth}w`);
|
|
29
|
+
} else {
|
|
30
|
+
srcs.push(`${src} ${srcsetWidth}w`);
|
|
31
|
+
}
|
|
27
32
|
} else if (srcsetWidth <= width) {
|
|
28
33
|
// avoid creating srcset sizes larger than intrinsic image width
|
|
29
|
-
|
|
34
|
+
if (format) {
|
|
35
|
+
srcs.push(`${imagesPath}/size/w${srcsetWidth}/format/${format}/${filename} ${srcsetWidth}w`);
|
|
36
|
+
} else {
|
|
37
|
+
srcs.push(`${imagesPath}/size/w${srcsetWidth}/${filename} ${srcsetWidth}w`);
|
|
38
|
+
}
|
|
30
39
|
}
|
|
31
40
|
});
|
|
32
41
|
|
|
@@ -42,6 +51,9 @@ const getSrcsetAttribute = function ({src, width, options}) {
|
|
|
42
51
|
|
|
43
52
|
srcsetWidths.forEach((srcsetWidth) => {
|
|
44
53
|
unsplashUrl.searchParams.set('w', srcsetWidth);
|
|
54
|
+
if (format) {
|
|
55
|
+
unsplashUrl.searchParams.set('fm', format);
|
|
56
|
+
}
|
|
45
57
|
srcs.push(`${unsplashUrl.href} ${srcsetWidth}w`);
|
|
46
58
|
});
|
|
47
59
|
|