7});
8
9export async function getRelatedPagesFromDatabase(pageId: string) {
10 try {
11 const response = await notion.databases.query({
12 database_id: Deno.env.get("GLANCE_CONTENT_DB_ID"),
13 filter: {
14 property: "Glancer demos", // the property in the Glancer content database that connects to the /demo
15 relation: {
16 contains: pageId, // the Glancer demos property holds the /demo page id to which it is related
25 });
26 // loop through the response and attach a "page_contents" object to each page
27 // page_content is the markdown in Notion in each "Glancer content" database page
28 for (const page of response.results) {
29 page.page_content = await getPageContents(page.id);
221```typescript
222export default async function (interval: Interval) {
223 const pages = await notion.databases.query({
224 database_id: Deno.env.get("GLANCE_DEMOS_DB_ID"),
225 });
226
244});
245
246const databaseId = Deno.env.get("GLANCE_DEMOS_DB_ID");
247```
248
250
251- `NOTION_API_KEY`: Notion integration token
252- `GLANCE_DEMOS_DB_ID`: Notion database ID for demo pages
253- `GLANCE_INTERACTIONS_DB_ID`: Notion database ID for interaction tracking
254
255## Project Conventions
291- `/api/*`: JSON data endpoints for frontend consumption
292- `/demo/*`: Personalized demo page serving with data injection
293- `/tasks/*`: Notion webhook processing and database updates
294
295## External Dependencies
300
301- **Email and Alerting**: Slack notifications and email alerts are configured within Notion
302- **Database Management**: Three core Notion databases power the application:
303 - **Glancer Demos Database**: Stores demo configurations, visitor information, and personalization data
304 - **Glancer Interactions Database**: Warehouses demo events, clicks, and user behavior analytics
305 - **Glancer Agents Database**: Manages agent assignments, availability, and contact information
306
307The Val Town application serves as an automation and presentation layer that extends Notion's native capabilities, providing real-time cobrowsing experiences while maintaining all data persistence and workflow management within the Notion ecosystem.
15# healthCheck.ts
16
17This cron runs through all of the databases listed in the Glancer Val's environment variables, queries them, and stores the results in blob storage. The output is JSON rendered as HTML, in an HTTP Val.
18
19We embed that HTTP Val into the Glancer app in Notion. This way, we can see the health of the integration between Notion and val.town from Notion, and it loads instantly, without having to query Notion.
6});
7
8// generic controller for a Notion database page
9export async function getPage(pageId: string) {
10 try {
43- blob storage for the cache, which enables snappy demos
44- cron(s) that keep the cache up-to-date
45- environment variables, which we use to store connection details for Notion, database ids for essential Notion databases (including the demos and interactions databases)
46- logging features
47
63
64- email and other alerting (e.g., Slack)
65- databases, including:
66 - the "Glancer demos" database
67 - the "Glancer interactions" database, which is where certain demo events (e.g., clicks) are warehoused and visualized
68 - the "Glancer agents" database, which handles agent assignments and management
69
70Note that this app integrates with Notion to extend Notion in important ways; namely, automation and logging. Please be clear about that.
12 status: "connected",
13 message: "Successfully connected to Notion API",
14 databases: response.results.map((db) => ({
15 title: db.title?.[0]?.plain_text || "Untitled",
16 id: db.id,
20
21// update the cache
22// this cron updates a blob that stores the JSON that shows the databases that val.town is connected to
23// we present that at root and embed that into Notion
24// to show everyone that the connection is healthy btw Notion and val.town
29 filter: {
30 property: "object",
31 value: "database",
32 },
33 });
12// that blob determines whether or not the cobrowsing button is ON or OFF
13export default async function (interval: Interval) {
14 // every page in the "Glancer demos" database should have it's own blob, so we have a cache for each demo
15 // this cron saves a blob for every page in the Demos DB
16 try {
17 // get notion pages with the databaseId
18 const pages = await notion.databases.query({
19 database_id: Deno.env.get("GLANCE_DEMOS_DB_ID"),
20 });
21 // for each page in the demo database, save a blob
22 for (const page of pages.results) {
23 const blobKey = await blobKeyForDemoCobrowseStatus(page.id);
2import { Client } from "npm:@notionhq/client";
3import { blobKeyForDemoCache } from "../../shared/utils/blobKeyForDemoCache.ts";
4import { getRelatedPagesFromDatabase } from "../controllers/relatedPages.controller.ts";
5
6// Initialize Notion client
11export default async function (interval: Interval) {
12 // this cron runs every minute
13 // it saves a blob for every page in the "Glancer demos" database
14 // that holds the Notion data and markup needed for every demo
15 // this way, to build a demo in the browser (ie., when /demo/:id is called),
18 const twoDaysAgo = new Date(Date.now() - 48 * 60 * 60 * 1000).toISOString();
19 try {
20 const pages = await notion.databases.query({
21 database_id: Deno.env.get("GLANCE_DEMOS_DB_ID"),
22 filter: {
23 // only get recently edited demos; we don't need to keep stale demos in the cache
29 });
30 console.log("pages to cache: ", pages.results.length);
31 // for each page in the demo database, save a blob
32 for (const page of pages.results) {
33 // remove properties that are not used in the demo and don't need to be saved to the blob
48 page.properties = scrubbed;
49
50 // get "Glancer content" database pages added to the demo page
51 const relatedPages = await getRelatedPagesFromDatabase(page.id);
52
53 // use the same blob key that /views/demo/demo.ts calls to build the page
6});
7
8export async function getDatabase(databaseId: string) {
9 // get database
10 try {
11 const database = await notion.databases.retrieve({
12 database_id: databaseId,
13 });
14 return database;
15 } catch (error: any) {
16 return {
1import { Hono } from "npm:hono";
2import { getDatabase } from "../../controllers/database.controller.ts";
3
4const app = new Hono();
9 // hit the controller to return data
10 try {
11 const data = await getDatabase(id);
12 //
13 console.log(data);