@xuda.io/account_module 1.2.2306 → 1.2.2307

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.
Files changed (2) hide show
  1. package/index.mjs +67 -0
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -608,6 +608,10 @@ const _MODULE_SUBSCRIPTIONS = [
608
608
  { key: 'preview', scope: 'resource' },
609
609
  { key: 'instance', scope: 'resource' },
610
610
  { key: 'backup', scope: 'resource' },
611
+ // UI-97: a registered domain renews yearly and a dedicated IPv4 is a monthly line,
612
+ // so both carry their own `cycle` on each item rather than being assumed monthly.
613
+ { key: 'domain', scope: 'resource' },
614
+ { key: 'ipv4', scope: 'resource' },
611
615
  // Xuda Verify (verify_module) has no PLAN_OBJ category: every check is metered
612
616
  // per call into verify_meter and nothing is charged to the account today.
613
617
  { key: 'trust_center', scope: 'metered' },
@@ -672,6 +676,65 @@ const _module_resource_items = async (uid) => {
672
676
  console.error('[get_module_subscriptions hosting]', e.message);
673
677
  }
674
678
 
679
+ // UI-97: domains registered THROUGH Xuda. A domain the customer merely connected
680
+ // (registered elsewhere, `connection_type: 'connected'`) costs nothing here, so the
681
+ // Stripe yearly price is what decides whether it is a subscription at all.
682
+ try {
683
+ const ret = await db_module.find_couch_query(
684
+ 'xuda_master',
685
+ { selector: { docType: 'domain_registration', owner_uid: uid }, limit: 500 },
686
+ true,
687
+ );
688
+ for (const dom of ret?.docs || []) {
689
+ const price = Number(dom.stripe_price_yr) || 0;
690
+ if (!price) continue;
691
+ const renews = dom.expires_at ? new Date(dom.expires_at) : null;
692
+ items.domain.push({
693
+ id: dom._id,
694
+ label: dom.name || dom._id,
695
+ // Auto renew off means it lapses at expiry, which is the one thing worth
696
+ // saying on a billing screen about a domain.
697
+ plan_name: dom.auto_renew === false
698
+ ? `Renews ${renews ? renews.toISOString().slice(0, 10) : 'at expiry'}, auto renew off`
699
+ : `Renews ${renews ? renews.toISOString().slice(0, 10) : 'yearly'}`,
700
+ price,
701
+ cycle: 'yr',
702
+ });
703
+ }
704
+ } catch (e) {
705
+ console.error('[get_module_subscriptions domains]', e.message);
706
+ }
707
+
708
+ // UI-97: dedicated IPv4. The per-address price depends on what it is doing
709
+ // (internal / external / parked), which is exactly how ipv4_module prices it, so the
710
+ // rate is read from PRICE_OBJ the same way rather than assumed.
711
+ try {
712
+ const prices = {
713
+ internal: Number(_conf.PRICE_OBJ?.ipv4?.internal ?? 2),
714
+ external: Number(_conf.PRICE_OBJ?.ipv4?.external ?? 10),
715
+ parked: Number(_conf.PRICE_OBJ?.ipv4?.parked ?? 5),
716
+ };
717
+ const ret = await db_module.find_couch_query(
718
+ 'xuda_ipv4',
719
+ { selector: { docType: 'ipv4_addr', account_id: uid }, limit: 1000 },
720
+ true,
721
+ );
722
+ for (const ip of ret?.docs || []) {
723
+ // A suspended address keeps billing at the rate it held, same as ipv4_module.
724
+ const bill = ip.state === 'suspended' ? ip.bill_as : ip.state;
725
+ const price = Number(prices[bill]) || 0;
726
+ if (!price) continue;
727
+ items.ipv4.push({
728
+ id: ip._id,
729
+ label: ip.address || ip._id,
730
+ plan_name: [bill, ip.target ? `on ${ip.target}` : ''].filter(Boolean).join(', '),
731
+ price,
732
+ });
733
+ }
734
+ } catch (e) {
735
+ console.error('[get_module_subscriptions ipv4]', e.message);
736
+ }
737
+
675
738
  try {
676
739
  const ret = await db_module.find_couch_query(
677
740
  'xuda_master',
@@ -730,6 +793,9 @@ export const get_module_subscriptions = async function (req = {}) {
730
793
  const base = { key: m.key, scope: m.scope, billable: billable(m.key), items: resource_items[m.key] || [] };
731
794
  if (m.scope === 'resource' || m.scope === 'metered') {
732
795
  const price = base.items.reduce((sum, i) => sum + Number(i.price || 0), 0);
796
+ // UI-97: a row is only yearly when everything in it is (domains). Anything
797
+ // else stays monthly, so a total is never a sum of two different cycles.
798
+ const cycle = base.items.length && base.items.every((i) => i.cycle === 'yr') ? 'yr' : 'mo';
733
799
  return {
734
800
  ...base,
735
801
  active: m.scope === 'metered' || base.items.length > 0,
@@ -737,6 +803,7 @@ export const get_module_subscriptions = async function (req = {}) {
737
803
  plan_id: '',
738
804
  plan_name: '',
739
805
  price,
806
+ cycle,
740
807
  };
741
808
  }
742
809
  const plan_id = account[m.field] || m.default_plan;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xuda.io/account_module",
3
- "version": "1.2.2306",
3
+ "version": "1.2.2307",
4
4
  "description": "Xuda Account Server Module",
5
5
  "main": "index.mjs",
6
6
  "dependencies": {