115_auth_identity_legacy_external_backfill.sql 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. CREATE OR REPLACE FUNCTION public.__migration_115_safe_legacy_metadata_jsonb(input_text TEXT)
  2. RETURNS JSONB
  3. LANGUAGE plpgsql
  4. AS $$
  5. DECLARE
  6. parsed JSONB;
  7. BEGIN
  8. IF input_text IS NULL OR BTRIM(input_text) = '' THEN
  9. RETURN '{}'::jsonb;
  10. END IF;
  11. BEGIN
  12. parsed := input_text::jsonb;
  13. EXCEPTION
  14. WHEN OTHERS THEN
  15. RETURN '{}'::jsonb;
  16. END;
  17. IF jsonb_typeof(parsed) = 'object' THEN
  18. RETURN parsed;
  19. END IF;
  20. RETURN jsonb_build_object('_legacy_metadata_raw_json', parsed);
  21. END;
  22. $$;
  23. DO $$
  24. BEGIN
  25. IF to_regclass('public.user_external_identities') IS NULL THEN
  26. RETURN;
  27. END IF;
  28. EXECUTE $sql$
  29. WITH legacy AS (
  30. SELECT
  31. uei.id,
  32. uei.user_id,
  33. BTRIM(uei.provider_user_id) AS provider_user_id,
  34. BTRIM(uei.provider_username) AS provider_username,
  35. BTRIM(uei.display_name) AS display_name,
  36. public.__migration_115_safe_legacy_metadata_jsonb(uei.metadata) AS metadata_json,
  37. uei.created_at,
  38. uei.updated_at
  39. FROM user_external_identities AS uei
  40. JOIN users AS u ON u.id = uei.user_id
  41. WHERE u.deleted_at IS NULL
  42. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'linuxdo'
  43. AND BTRIM(COALESCE(uei.provider_user_id, '')) <> ''
  44. ),
  45. legacy_subjects AS (
  46. SELECT
  47. provider_user_id AS provider_subject,
  48. COUNT(DISTINCT user_id) AS distinct_user_count
  49. FROM legacy
  50. GROUP BY provider_user_id
  51. ),
  52. canonical_legacy AS (
  53. SELECT
  54. legacy.*,
  55. ROW_NUMBER() OVER (
  56. PARTITION BY legacy.provider_user_id
  57. ORDER BY COALESCE(legacy.updated_at, legacy.created_at, NOW()) DESC, legacy.id DESC
  58. ) AS canonical_row_num
  59. FROM legacy
  60. JOIN legacy_subjects AS subjects
  61. ON subjects.provider_subject = legacy.provider_user_id
  62. AND subjects.distinct_user_count = 1
  63. )
  64. INSERT INTO auth_identities (
  65. user_id,
  66. provider_type,
  67. provider_key,
  68. provider_subject,
  69. verified_at,
  70. metadata
  71. )
  72. SELECT
  73. legacy.user_id,
  74. 'linuxdo',
  75. 'linuxdo',
  76. legacy.provider_user_id,
  77. COALESCE(legacy.updated_at, legacy.created_at, NOW()),
  78. legacy.metadata_json || jsonb_build_object(
  79. 'legacy_identity_id', legacy.id,
  80. 'provider_user_id', legacy.provider_user_id,
  81. 'provider_username', legacy.provider_username,
  82. 'display_name', legacy.display_name,
  83. 'migration', '115_auth_identity_legacy_external_backfill'
  84. )
  85. FROM canonical_legacy AS legacy
  86. WHERE legacy.canonical_row_num = 1
  87. ON CONFLICT (provider_type, provider_key, provider_subject) DO NOTHING;
  88. $sql$;
  89. EXECUTE $sql$
  90. WITH legacy AS (
  91. SELECT
  92. uei.id,
  93. uei.user_id,
  94. BTRIM(uei.provider_user_id) AS provider_user_id,
  95. BTRIM(uei.provider_union_id) AS provider_union_id,
  96. BTRIM(uei.provider_username) AS provider_username,
  97. BTRIM(uei.display_name) AS display_name,
  98. public.__migration_115_safe_legacy_metadata_jsonb(uei.metadata) AS metadata_json,
  99. uei.created_at,
  100. uei.updated_at
  101. FROM user_external_identities AS uei
  102. JOIN users AS u ON u.id = uei.user_id
  103. WHERE u.deleted_at IS NULL
  104. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat'
  105. AND BTRIM(COALESCE(uei.provider_union_id, '')) <> ''
  106. ),
  107. legacy_subjects AS (
  108. SELECT
  109. provider_union_id AS provider_subject,
  110. COUNT(DISTINCT user_id) AS distinct_user_count
  111. FROM legacy
  112. GROUP BY provider_union_id
  113. ),
  114. canonical_legacy AS (
  115. SELECT
  116. legacy.*,
  117. ROW_NUMBER() OVER (
  118. PARTITION BY legacy.provider_union_id
  119. ORDER BY COALESCE(legacy.updated_at, legacy.created_at, NOW()) DESC, legacy.id DESC
  120. ) AS canonical_row_num
  121. FROM legacy
  122. JOIN legacy_subjects AS subjects
  123. ON subjects.provider_subject = legacy.provider_union_id
  124. AND subjects.distinct_user_count = 1
  125. )
  126. INSERT INTO auth_identities (
  127. user_id,
  128. provider_type,
  129. provider_key,
  130. provider_subject,
  131. verified_at,
  132. metadata
  133. )
  134. SELECT
  135. legacy.user_id,
  136. 'wechat',
  137. 'wechat-main',
  138. legacy.provider_union_id,
  139. COALESCE(legacy.updated_at, legacy.created_at, NOW()),
  140. legacy.metadata_json || jsonb_build_object(
  141. 'legacy_identity_id', legacy.id,
  142. 'openid', legacy.provider_user_id,
  143. 'unionid', legacy.provider_union_id,
  144. 'provider_user_id', legacy.provider_user_id,
  145. 'provider_union_id', legacy.provider_union_id,
  146. 'provider_username', legacy.provider_username,
  147. 'display_name', legacy.display_name,
  148. 'migration', '115_auth_identity_legacy_external_backfill'
  149. )
  150. FROM canonical_legacy AS legacy
  151. WHERE legacy.canonical_row_num = 1
  152. ON CONFLICT (provider_type, provider_key, provider_subject) DO NOTHING;
  153. $sql$;
  154. EXECUTE $sql$
  155. WITH legacy AS (
  156. SELECT
  157. uei.user_id,
  158. BTRIM(uei.provider_user_id) AS provider_user_id,
  159. BTRIM(uei.provider_union_id) AS provider_union_id,
  160. BTRIM(COALESCE(meta.metadata_json ->> 'channel', '')) AS channel,
  161. BTRIM(COALESCE(meta.metadata_json ->> 'channel_app_id', meta.metadata_json ->> 'appid', meta.metadata_json ->> 'app_id', '')) AS channel_app_id,
  162. meta.metadata_json
  163. FROM user_external_identities AS uei
  164. JOIN users AS u ON u.id = uei.user_id
  165. CROSS JOIN LATERAL (
  166. SELECT public.__migration_115_safe_legacy_metadata_jsonb(uei.metadata) AS metadata_json
  167. ) AS meta
  168. WHERE u.deleted_at IS NULL
  169. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat'
  170. AND BTRIM(COALESCE(uei.provider_union_id, '')) <> ''
  171. ),
  172. legacy_subjects AS (
  173. SELECT
  174. provider_union_id AS provider_subject,
  175. COUNT(DISTINCT user_id) AS distinct_user_count
  176. FROM legacy
  177. GROUP BY provider_union_id
  178. )
  179. INSERT INTO auth_identity_channels (
  180. identity_id,
  181. provider_type,
  182. provider_key,
  183. channel,
  184. channel_app_id,
  185. channel_subject,
  186. metadata
  187. )
  188. SELECT
  189. ai.id,
  190. 'wechat',
  191. 'wechat-main',
  192. legacy.channel,
  193. legacy.channel_app_id,
  194. legacy.provider_user_id,
  195. legacy.metadata_json || jsonb_build_object(
  196. 'openid', legacy.provider_user_id,
  197. 'unionid', legacy.provider_union_id,
  198. 'migration', '115_auth_identity_legacy_external_backfill'
  199. )
  200. FROM legacy
  201. JOIN legacy_subjects AS subjects
  202. ON subjects.provider_subject = legacy.provider_union_id
  203. AND subjects.distinct_user_count = 1
  204. JOIN auth_identities AS ai
  205. ON ai.user_id = legacy.user_id
  206. AND ai.provider_type = 'wechat'
  207. AND ai.provider_key = 'wechat-main'
  208. AND ai.provider_subject = legacy.provider_union_id
  209. WHERE legacy.channel <> ''
  210. AND legacy.channel_app_id <> ''
  211. AND legacy.provider_user_id <> ''
  212. ON CONFLICT DO NOTHING;
  213. $sql$;
  214. EXECUTE $sql$
  215. INSERT INTO auth_identity_migration_reports (report_type, report_key, details)
  216. SELECT
  217. 'wechat_openid_only_requires_remediation',
  218. 'legacy_external_identity:' || legacy.id::text,
  219. legacy.metadata_json || jsonb_build_object(
  220. 'legacy_identity_id', legacy.id,
  221. 'user_id', legacy.user_id,
  222. 'openid', legacy.provider_user_id,
  223. 'reason', 'legacy user_external_identities row only has openid and cannot be canonicalized offline',
  224. 'migration', '115_auth_identity_legacy_external_backfill'
  225. )
  226. FROM (
  227. SELECT
  228. uei.id,
  229. uei.user_id,
  230. BTRIM(uei.provider_user_id) AS provider_user_id,
  231. public.__migration_115_safe_legacy_metadata_jsonb(uei.metadata) AS metadata_json
  232. FROM user_external_identities AS uei
  233. JOIN users AS u ON u.id = uei.user_id
  234. WHERE u.deleted_at IS NULL
  235. AND LOWER(BTRIM(COALESCE(uei.provider, ''))) = 'wechat'
  236. AND BTRIM(COALESCE(uei.provider_user_id, '')) <> ''
  237. AND BTRIM(COALESCE(uei.provider_union_id, '')) = ''
  238. ) AS legacy
  239. ON CONFLICT (report_type, report_key) DO NOTHING;
  240. $sql$;
  241. END $$;
  242. INSERT INTO auth_identity_migration_reports (report_type, report_key, details)
  243. SELECT
  244. 'wechat_openid_only_requires_remediation',
  245. 'synthetic_auth_identity:' || ai.id::text,
  246. COALESCE(ai.metadata, '{}'::jsonb) || jsonb_build_object(
  247. 'auth_identity_id', ai.id,
  248. 'user_id', ai.user_id,
  249. 'provider_subject', ai.provider_subject,
  250. 'reason', 'synthetic wechat auth identity still lacks unionid metadata and needs remediation',
  251. 'migration', '115_auth_identity_legacy_external_backfill'
  252. )
  253. FROM auth_identities AS ai
  254. WHERE ai.provider_type = 'wechat'
  255. AND COALESCE(ai.metadata ->> 'backfill_source', '') = 'synthetic_email'
  256. AND BTRIM(COALESCE(ai.metadata ->> 'unionid', '')) = ''
  257. ON CONFLICT (report_type, report_key) DO NOTHING;
  258. DROP FUNCTION IF EXISTS public.__migration_115_safe_legacy_metadata_jsonb(TEXT);