diff --git a/functions/[[accountId]]/widget/[[index]].js b/functions/[[accountId]]/widget/[[index]].js deleted file mode 100644 index e5795b2f..00000000 --- a/functions/[[accountId]]/widget/[[index]].js +++ /dev/null @@ -1,219 +0,0 @@ -import { - socialGet, - imageToUrl, - wrapImage, - DefaultProfileImage, -} from "../../common"; - -class MetaTitleInjector { - constructor({ title }) { - this.title = title; - } - - element(element) { - element.setAttribute("content", this.title); - } -} - -class MetaImageInjector { - constructor({ image, authorImage }) { - this.image = image; - this.authorImage = authorImage; - } - - element(element) { - if (this.image) { - element.setAttribute("content", this.image); - } else if (this.authorImage) { - element.setAttribute("content", this.authorImage); - } - } -} - -class MetaTwitterCardInjector { - constructor({ image }) { - this.image = image; - } - - element(element) { - if (!this.image) { - element.setAttribute("content", "summary"); - } - } -} - -class MetaDescriptionInjector { - constructor({ shortDescription }) { - this.shortDescription = shortDescription; - } - - element(element) { - element.setAttribute( - "content", - this.shortDescription?.replaceAll("\n", " ") - ); - } -} - -class TitleInjector { - constructor({ title }) { - this.title = title; - } - - element(element) { - element.setInnerContent(this.title); - } -} - -class NoscriptDescriptionInjector { - constructor({ description }) { - this.description = description; - } - - element(element) { - element.setInnerContent(this.description); - } -} - -function defaultData() { - const image = "https://near.social/assets/logo.png"; - const title = "Near Social"; - const description = - "Decentralized Customizable Social Network on NEAR Protocol"; - return { - image, - title, - description, - }; -} - -async function postData(env, url, data, isPost) { - const accountId = url.searchParams.get("accountId"); - const blockHeight = url.searchParams.get("blockHeight"); - const [content, name, authorImage] = await Promise.all([ - socialGet( - `${accountId}/post/${isPost ? "main" : "comment"}`, - blockHeight, - true - ), - socialGet(`${accountId}/profile/name`), - socialGet(`${accountId}/profile/image/**`), - ]); - - data.raw = content; - data.description = content?.text || ""; - data.image = await imageToUrl(env, content?.image); - if (!data.image) { - data.authorImage = await imageToUrl(env, authorImage); - } - data.title = isPost - ? `Post by ${name ?? accountId} | Near Social` - : `Comment by ${name ?? accountId} | Near Social`; - data.accountName = name; - data.accountId = accountId; -} - -async function profileData(env, url, data) { - const accountId = url.searchParams.get("accountId"); - const profile = await socialGet(`${accountId}/profile/**`); - - const name = profile?.name; - data.raw = profile; - data.description = - profile?.description || `Profile of ${accountId} on Near Social`; - data.image = await imageToUrl(env, profile?.image); - data.authorImage = data.image || wrapImage(DefaultProfileImage); - data.title = name - ? `${name} (${accountId}) | Near Social` - : `${accountId} | Near Social`; - data.accountName = name; - data.accountId = accountId; -} - -async function widgetData(env, url, data) { - const parts = url.pathname.split("/"); - const accountId = parts[1]; - const widgetId = parts[3]; - const metadata = await socialGet( - `${accountId}/widget/${widgetId}/metadata/**` - ); - - const name = metadata?.name || widgetId; - data.raw = metadata; - data.description = - metadata?.description || `Component ${name} created by ${accountId}`; - data.image = await imageToUrl(env, metadata?.image); - data.title = `${name} by ${accountId} | Near Social`; - data.accountName = name; - data.accountId = accountId; -} - -async function sourceData(env, url, data) { - const key = url.searchParams.get("src"); - const parts = key.split("/"); - const accountId = parts[0]; - const blockHeight = url.searchParams.get("blockHeight"); - const [source, image] = await Promise.all([ - socialGet(key, blockHeight), - socialGet(`${key}/metadata/image/**`), - ]); - - data.raw = source; - data.description = source || "The source code is not available."; - data.image = null; - data.authorImage = await imageToUrl(env, image); - data.title = `Source code of ${key} at block height ${blockHeight} | Near Social`; - data.accountId = accountId; -} - -async function generateData(env, url) { - const data = defaultData(); - try { - if ( - url.pathname === "/mob.near/widget/MainPage.Post.Page" || - url.pathname === "/mob.near/widget/MainPage.N.Post.Page" || - url.pathname === "/near/widget/PostPage" - ) { - await postData(env, url, data, true); - } else if ( - url.pathname === "/mob.near/widget/MainPage.Comment.Page" || - url.pathname === "/mob.near/widget/MainPage.N.Comment.Page" - ) { - await postData(env, url, data, false); - } else if (url.pathname === "/mob.near/widget/ProfilePage") { - await profileData(env, url, data); - } else if (url.pathname === "/mob.near/widget/WidgetSource") { - await sourceData(env, url, data); - } else { - await widgetData(env, url, data); - } - } catch (e) { - console.error(e); - } - data.shortDescription = data.description.slice(0, 250); - - return data; -} - -export async function onRequest({ request, next, env }) { - const url = new URL(request.url); - if ( - url.pathname.split("/").length < 4 || - url.pathname.endsWith(".bundle.js") - ) { - return next(); - } - const data = await generateData(env, url); - return ( - new HTMLRewriter() - .on('meta[property="og:title"]', new MetaTitleInjector(data)) - .on('meta[property="og:image"]', new MetaImageInjector(data)) - .on('meta[name="twitter:card"]', new MetaTwitterCardInjector(data)) - .on('meta[property="og:description"]', new MetaDescriptionInjector(data)) - .on('meta[name="description"]', new MetaDescriptionInjector(data)) - // .on("head", new MetaTagInjector(data)) - .on("title", new TitleInjector(data)) - .on("noscript", new NoscriptDescriptionInjector(data)) - .transform(await next()) - ); -} diff --git a/functions/common.js b/functions/common.js deleted file mode 100644 index df6cf7aa..00000000 --- a/functions/common.js +++ /dev/null @@ -1,177 +0,0 @@ -import { Buffer } from "node:buffer"; - -export async function socialIndex(action, key, options) { - const request = await fetch("https://api.near.social/index", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - action, - key, - options, - }), - }); - return await request.json(); -} - -export async function socialKeys(keys, blockHeight, options) { - const request = await fetch("https://api.near.social/keys", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - keys: [keys], - blockHeight, - options, - }), - }); - return await request.json(); -} - -export async function socialGet(keys, blockHeight, parse) { - const request = await fetch("https://api.near.social/get", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - keys: [keys], - blockHeight, - }), - }); - let data = await request.json(); - const parts = keys.split("/"); - for (let i = 0; i < parts.length; i++) { - const part = parts[i]; - if (part === "*" || part === "**") { - break; - } - data = data?.[part]; - } - if (parse) { - try { - data = JSON.parse(data); - } catch (e) { - return null; - } - } - return data; -} - -export async function viewCall({ contractId, method, args }) { - const res = await fetch("https://rpc.mainnet.near.org", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - jsonrpc: "2.0", - id: "dontcare", - method: "query", - params: { - request_type: "call_function", - finality: "final", - account_id: contractId, - method_name: method, - args_base64: btoa(JSON.stringify(args)), - }, - }), - }); - const json = await res.json(); - const result = Buffer.from(json.result.result).toString("utf-8"); - return JSON.parse(result); -} - -export async function nftToImageUrl({ contractId, tokenId }) { - const [token, nftMetadata] = await Promise.all([ - tokenId - ? viewCall({ - contractId, - method: "nft_token", - args: { token_id: tokenId }, - }) - : Promise.resolve(null), - viewCall({ - contractId, - method: "nft_metadata", - args: {}, - }), - ]); - - if (!tokenId) { - return nftMetadata.icon; - } - - const tokenMetadata = token?.metadata || {}; - const tokenMedia = tokenMetadata.media || ""; - - let imageUrl = - tokenMedia.startsWith("https://") || - tokenMedia.startsWith("http://") || - tokenMedia.startsWith("data:image") - ? tokenMedia - : nftMetadata.base_uri - ? `${nftMetadata.base_uri}/${tokenMedia}` - : tokenMedia.startsWith("Qm") || tokenMedia.startsWith("ba") - ? `https://ipfs.near.social/ipfs/${tokenMedia}` - : tokenMedia; - - if (!tokenMedia && tokenMetadata.reference) { - const metadataUrl = - nftMetadata.base_uri === "https://arweave.net" && - !tokenMetadata.reference.startsWith("https://") - ? `${nftMetadata.base_uri}/${tokenMetadata.reference}` - : tokenMetadata.reference.startsWith("https://") || - tokenMetadata.reference.startsWith("http://") - ? tokenMetadata.reference - : tokenMetadata.reference.startsWith("ar://") - ? `https://arweave.net/${tokenMetadata.reference.split("//")[1]}` - : null; - if (metadataUrl) { - const res = await fetch(metadataUrl); - const json = await res.json(); - imageUrl = json.media; - } - } - - return imageUrl; -} - -export function wrapImage(url) { - return url ? `https://i.near.social/large/${url}` : null; -} - -export async function internalImageToUrl(env, image) { - if (image?.url) { - return image.url; - } else if (image?.ipfs_cid) { - return `https://ipfs.near.social/ipfs/${image.ipfs_cid}`; - } else if (image?.nft) { - try { - const { contractId, tokenId } = image.nft; - const NftKV = env.NftKV; - const path = tokenId ? `${contractId}/${tokenId}` : contractId; - - let imageUrl = await NftKV.get(path); - if (!imageUrl) { - imageUrl = await nftToImageUrl({ contractId, tokenId }); - if (imageUrl) { - await NftKV.put(path, imageUrl); - } - } - return imageUrl; - } catch (e) { - console.log(e); - } - } - return null; -} - -export async function imageToUrl(env, image) { - return wrapImage(await internalImageToUrl(env, image)); -} - -export const DefaultProfileImage = - "https://ipfs.near.social/ipfs/bafkreibmiy4ozblcgv3fm3gc6q62s55em33vconbavfd2ekkuliznaq3zm"; diff --git a/functions/magic/img/account/[index].js b/functions/magic/img/account/[index].js deleted file mode 100644 index 2b1ed259..00000000 --- a/functions/magic/img/account/[index].js +++ /dev/null @@ -1,30 +0,0 @@ -import { - DefaultProfileImage, - internalImageToUrl, - socialGet, -} from "../../../common"; - -export async function onRequest({ request, next, env }) { - const url = new URL(request.url); - const parts = url.pathname.split("/"); - if (parts.length !== 5) { - return next(); - } - const accountId = parts[4]; - const image = await socialGet(`${accountId}/profile/image/**`); - - const destinationURL = await internalImageToUrl(env, image); - - if (!destinationURL) { - // return status 203, which means "non-authoritative information" - return new Response(DefaultProfileImage, { - status: 203, - }); - } - - return new Response(destinationURL, { - headers: { - "content-type": "text/plain;charset=UTF-8", - }, - }); -} diff --git a/functions/magic/img/nft/[[index]].js b/functions/magic/img/nft/[[index]].js deleted file mode 100644 index f0e812f0..00000000 --- a/functions/magic/img/nft/[[index]].js +++ /dev/null @@ -1,28 +0,0 @@ -import { internalImageToUrl } from "../../../common"; - -export async function onRequest({ request, next, env }) { - const url = new URL(request.url); - const parts = url.pathname.split("/"); - if (parts.length !== 5 && parts.length !== 6) { - return next(); - } - const contractId = parts[4]; - const tokenId = parts[5]; - - const destinationURL = await internalImageToUrl(env, { - nft: { - contractId, - tokenId, - }, - }); - - return destinationURL - ? new Response(destinationURL, { - headers: { - "content-type": "text/plain;charset=UTF-8", - }, - }) - : new Response(null, { - status: 404, - }); -} diff --git a/functions/sitemap/index.js b/functions/sitemap/index.js deleted file mode 100644 index 04cefdbc..00000000 --- a/functions/sitemap/index.js +++ /dev/null @@ -1,27 +0,0 @@ -export async function onRequest({ request, next, env }) { - return new Response( - ` - - - https://near.social/sitemap/posts/0 - - - https://near.social/sitemap/widgets/ - - - https://near.social/sitemap/profiles/ - - - https://near.social/sitemap/sources/0 - - - https://near.social/sitemap/sources/50000 - -`, - { - headers: { - "content-type": "application/xml;charset=UTF-8", - }, - } - ); -} diff --git a/functions/sitemap/posts/[index].js b/functions/sitemap/posts/[index].js deleted file mode 100644 index 53d0e24a..00000000 --- a/functions/sitemap/posts/[index].js +++ /dev/null @@ -1,37 +0,0 @@ -import { socialIndex } from "../../common"; - -const Limit = 50000; - -export const generateSitemapPosts = async (env, offset) => { - const posts = await socialIndex("post", "main", {}); - const urls = posts.map( - (post) => - ` - https://near.social/mob.near/widget/MainPage.N.Post.Page?accountId=${post.accountId}&blockHeight=${post.blockHeight} - monthly - ` - ); - console.log("urls count", urls.length); - return urls.slice(offset, offset + Limit).join("\n"); -}; - -export async function onRequest({ request, env, next }) { - const url = new URL(request.url); - const parts = url.pathname.split("/"); - if (parts.length !== 4) { - return next(); - } - const offset = parseInt(parts[3]); - - return new Response( - ` - -${await generateSitemapPosts(env, offset)} -`, - { - headers: { - "content-type": "application/xml;charset=UTF-8", - }, - } - ); -} diff --git a/functions/sitemap/profiles/index.js b/functions/sitemap/profiles/index.js deleted file mode 100644 index 632228bc..00000000 --- a/functions/sitemap/profiles/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import { socialKeys } from "../../common"; - -export const generateSitemapProfiles = async (env) => { - const data = await socialKeys("*/profile"); - const accountIds = Object.keys(data); - return accountIds - .map( - (accountId) => - ` - https://near.social/mob.near/widget/ProfilePage?accountId=${accountId} - monthly - ` - ) - .join("\n"); -}; - -export async function onRequest({ env }) { - return new Response( - ` - -${await generateSitemapProfiles(env)} -`, - { - headers: { - "content-type": "application/xml;charset=UTF-8", - }, - } - ); -} diff --git a/functions/sitemap/sources/[index].js b/functions/sitemap/sources/[index].js deleted file mode 100644 index 1e0bf30c..00000000 --- a/functions/sitemap/sources/[index].js +++ /dev/null @@ -1,50 +0,0 @@ -import { socialKeys } from "../../common"; - -const MinBlockHeight = 75942518; -const LIMIT = 50000; - -export const generateSitemapSources = async (env, offset) => { - const data = await socialKeys("*/widget/*", null, { - return_type: "History", - }); - const urls = Object.entries(data) - .map(([accountId, widget]) => - Object.entries(widget.widget) - .map(([widgetId, blockHeights]) => - blockHeights - .filter((blockHeight) => blockHeight >= MinBlockHeight) - .map( - (blockHeight) => - ` - https://near.social/mob.near/widget/WidgetSource?src=${accountId}/widget/${widgetId}&blockHeight=${blockHeight} - never - ` - ) - ) - .flat() - ) - .flat(); - console.log("urls count", urls.length); - return urls.slice(offset, offset + LIMIT).join("\n"); -}; - -export async function onRequest({ env, request, next }) { - const url = new URL(request.url); - const parts = url.pathname.split("/"); - if (parts.length !== 4) { - return next(); - } - const offset = parseInt(parts[3]); - - return new Response( - ` - -${await generateSitemapSources(env, offset)} -`, - { - headers: { - "content-type": "application/xml;charset=UTF-8", - }, - } - ); -} diff --git a/functions/sitemap/widgets/index.js b/functions/sitemap/widgets/index.js deleted file mode 100644 index dc3de546..00000000 --- a/functions/sitemap/widgets/index.js +++ /dev/null @@ -1,31 +0,0 @@ -import { socialKeys } from "../../common"; - -export const generateSitemapWidgets = async (env) => { - const data = await socialKeys("*/widget/*/metadata"); - return Object.entries(data) - .map(([accountId, widget]) => - Object.keys(widget.widget).map( - (widgetId) => - ` - https://near.social/${accountId}/widget/${widgetId} - monthly - ` - ) - ) - .flat() - .join("\n"); -}; - -export async function onRequest({ env }) { - return new Response( - ` - -${await generateSitemapWidgets(env)} -`, - { - headers: { - "content-type": "application/xml;charset=UTF-8", - }, - } - ); -} diff --git a/package.json b/package.json index fcf60cf9..c235c9fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "0.18.1", + "version": "0.18.3", "homepage": "/", "private": true, "dependencies": { @@ -15,11 +15,11 @@ "@near-wallet-selector/neth": "^8.5.0", "@near-wallet-selector/nightly": "^8.5.0", "@near-wallet-selector/sender": "^8.5.0", - "@web3-onboard/core": "^2.16.2", - "@web3-onboard/injected-wallets": "^2.8.4", - "@web3-onboard/ledger": "^2.4.4", - "@web3-onboard/react": "^2.7.2", - "@web3-onboard/walletconnect": "^2.3.5", + "@web3-onboard/core": "^2.20.2", + "@web3-onboard/injected-wallets": "^2.10.1", + "@web3-onboard/ledger": "^2.4.6", + "@web3-onboard/react": "^2.8.7", + "@web3-onboard/walletconnect": "^2.3.9", "big.js": "^6.1.1", "bn.js": "^5.1.1", "bootstrap": "^5.3.1", @@ -28,7 +28,7 @@ "error-polyfill": "^0.1.2", "local-storage": "^2.0.0", "near-api-js": "^2.1.3", - "near-social-vm": "git+https://github.com/NearSocial/VM.git#2.5.1", + "near-social-vm": "git+https://github.com/NearSocial/VM.git#f3b74c72d8af79d39b36bb13869a940f2b195844", "near-social-vm-types": "^1.0.0", "prettier": "^2.7.1", "qrcode.react": "^3.1.0", diff --git a/public/card.png b/public/card.png new file mode 100644 index 00000000..714538a2 Binary files /dev/null and b/public/card.png differ diff --git a/public/favicon.png b/public/favicon.png index 00b44cd2..8fe843a8 100644 Binary files a/public/favicon.png and b/public/favicon.png differ diff --git a/public/index.html b/public/index.html index fa6c5a04..8bd76b7b 100644 --- a/public/index.html +++ b/public/index.html @@ -13,15 +13,14 @@ - - + - - - Near Social + + + Blockchain Operating System (BOS)