Skip to content

Latest commit

 

History

443 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SciTeens Logo

Welcome to the SciTeens Repo!

This is the work-in-progress version of the open-source SciTeens platform. This application is built in Next JS (A Server-Side Rendered React JS framework.) If you encounter issues with the website, please fork our site and commit fixes or detail the issues in the issues tab on GitHub. If you just want to explore the code, then enjoy!

Learning

If you'd like to learn about web development, you can explore this repository and the tools that we use. We've provided the tools that we use below, as well as some good learning guides for getting started with each of these tools.

  • Tailwind CSS
    Tailwind CSS is a class-based CSS framework that allows us to style our website. It's pretty straightforward to learn, the documentation is fantastic, and you can try it yourself without downloading anything.
  • React
    React is a framework that allows us to create the functionality for each of our pages. To get started, be sure to check out this website
  • Next JS
    Next JS is built on top of React, and allows users to find our website easier via search engines like Google using a concept called Server Side Rendering or Static Site Generation (SSR or SSG). To learn more about Next JS and the concepts of SSR and SSG, check out the Next JS docs.
  • Firebase
    Firebase is a fantastic tool for easily managing the back-end of an application from the front-end. Firebase takes care of managing users, website analytics, as well as storing data in a NoSQL database. To get started with learning Firebase, we recommend checking out this website or watching this video. Also, be sure to check out the official Firebase YouTube channel https://www.youtube.com/c/firebase
  • Docker
    To host our website, we use a tool called Docker paired with Google Cloud Run. These tools allow us to "bundle" our site (almost like we're packaging our website up into a box) and then putting that "box" on Google Cloud Run for other people to access at https://sciteens.com.

Getting Started

Before you begin, make sure that you have both Git and Node JS installed on your computer. To get started with the code, follow the steps below:

  1. Clone the repository by typing in git clone https://github.com/Sci-Teens/sciteens.git into your command line. If you don't have access, you can fork the repository instead.
  2. Type in cd sciteens to the command line and hit enter.
  3. Type in corepack pnpm install to the command line and hit enter. This will download all necessary packages.
  4. Type in corepack pnpm dev and visit localhost:3000 in your browser. This will show the development build!
  5. If you encounter an error at the step above, it's likely because you don't have access to the API keys. If you'd like to join the team to contribute to the website, reach out!

Opportunity discovery

The Discover Opportunity Sources GitHub Actions workflow runs only when an operator starts it. It has no scheduled trigger.

  1. Open the repository's Actions tab.
  2. Select Discover Opportunity Sources.
  3. Select Run workflow.
  4. Leave execute unchecked for a dry run.

If you want to write the results to Firestore, select execute. The write run creates active sources for accepted programs and records final rejections. Each run performs a new search. A write run does not reuse results from a previous dry run.

Discovery uses the scrape-opportunities environment and its existing GCP variables. Dry runs still call the model and fetch public pages. The separate Scrape Opportunities workflow continues its weekly refresh of active sources.

Discovery requires a successful HTTPS page fetch before the model verdict. An official source URL must match a successful fetch from that verification. Name mismatches remain eligible for another discovery run, including previous mismatch rejection records. Discovery decisions use discoveryReasoning and discoveryRedFlags. Scraper updates retain them.

Scheduled opportunity scraper

GitHub Actions schedules the opportunity scraper each Monday at 06:00 UTC. The scraper completes the batch and saves successful results even when another source fails. Any failed source gives the process a nonzero exit status, so GitHub Actions marks the run as failed.

If every prefetched page fails, the scraper stops extraction before the model call. When the transport supplies an error code, the failure record retains it. Public-address checks and URL provenance checks still apply.

Both scripts use the same constrained browser transport. Initial redirects pass through the public-address guard before the browser receives the page. The browser blocks later redirects, service workers, and WebSockets. A blocked main-page redirect causes a fetch failure, not publication.

Scheduled social posts

GitHub Actions schedules the opportunity deadline post each Monday at 10:00 UTC. It selects dated opportunities due in the next 30 days. Each carousel places the nearest deadline first. The workflow creates another ordered carousel when more than nine opportunities qualify.

Create a social-posts GitHub environment. Set these variables:

  • GCP_PROJECT_ID
  • GCP_WIF_PROVIDER
  • GCP_SCRAPER_SA
  • SITE_URL

The Google Cloud Workload Identity provider must accept the identity for the social-posts environment:

repo:Sci-Teens/sciteens:environment:social-posts

Keep the repository, branch, and workflow restrictions in the provider's attribute condition. The service account must also grant roles/iam.workloadIdentityUser to this identity. The scrape-opportunities environment uses a different identity. Access for that environment does not authorize social-posts.

If authentication reports The given credential is rejected by the attribute condition., inspect the provider's attribute condition before the service account policy. The scheduler does not start when this check fails.

Set the BUFFER_API_KEY secret. The workflow uses the Directed Relic Buffer project by default. Set BUFFER_ORGANIZATION_NAME only to use another project.

If Directed Relic has more than one Instagram channel, set the BUFFER_CHANNEL_ID secret. The Buffer channel must be connected. Set the channel posting schedule in Buffer. The workflow uses that queue, so Buffer selects the next configured posting time.

Monthly newsletter

SciTeens sends the monthly newsletter through Plunk campaigns. The newsletter uses React Email templates in functions/lib/emailTemplates.js.

The system uses two Plunk static segments:

  • SciTeens - Transactional contains website account contacts.
  • SciTeens - Newsletter contains confirmed newsletter subscribers.

The newsletter segment and Plunk subscription state control newsletter consent. A newsletter opt-out does not stop transactional email delivery.

Initial setup

  1. Verify the sciteens.org sending domain in Plunk.
  2. Create a Plunk secret API key (sk_*) that can manage contacts, segments, and campaigns.
  3. Set the API key as the Firebase PLUNK_SECRET_KEY secret.
  4. Deploy the Cloud Functions.
  5. Authenticate the local Google Cloud CLI with Application Default Credentials.
  6. Set PLUNK_SECRET_KEY in the local shell.
  7. Run the contact migration.
firebase functions:secrets:set PLUNK_SECRET_KEY
firebase deploy --only functions

export GCP_PROJECT_ID=<gcp-project-id>
export PLUNK_SECRET_KEY=<plunk-secret-key>
gcloud auth application-default login
pnpm newsletter:sync -- --project "$GCP_PROJECT_ID"

The first sync, confirmation, or campaign that needs it creates the Newsletter segment. The first sync moves website accounts and newsletter records from Firestore into their correct segments. To migrate the existing Resend address book, run the one-time Resend migration with both API keys in your local shell:

export RESEND_APIKEY=<resend-api-key>
export PLUNK_SECRET_KEY=<plunk-secret-key>
pnpm newsletter:migrate-resend -- --project "$GCP_PROJECT_ID"

The migration subscribes every Resend contact except contacts already globally unsubscribed in Resend or through the SciTeens newsletter unsubscribe page. The first sync creates replacement unsubscribe tokens only for unmarked newsletter subscribers. Later syncs update segment membership without token rotation.

Monthly workflow

Create a JSON file for the issue. The file must contain these fields:

  • name
  • subject
  • preview
  • title
  • opening
  • featuredArticle
  • featuredProject

opening is an array of one to three paragraphs. Each featured item needs title, description, and an HTTPS href. An optional featured image needs imageUrl and imageAlt.

You can omit opportunities. When you omit it, the command selects up to six dated opportunities in the next 30 days. The query uses the same deadline window as the Instagram post process.

Use this shape when you create a file:

{
  "name": "Month Year",
  "subject": "The subject for this issue",
  "preview": "The inbox preview text",
  "title": "The newsletter title",
  "opening": [
    "The first opening paragraph.",
    "The optional second opening paragraph."
  ],
  "featuredArticle": {
    "title": "The article title",
    "description": "The article description",
    "href": "https://sciteens.org/article/<slug>"
  },
  "featuredProject": {
    "title": "The project title",
    "description": "The project description",
    "href": "https://sciteens.org/project/<slug>"
  }
}

First, create an HTML preview.

pnpm newsletter:create -- \
  --input monthly-newsletter.json \
  --project "$GCP_PROJECT_ID" \
  --dry-run

The command writes an HTML file beside the JSON file. Read the HTML file before you create a Plunk draft.

Then create a draft broadcast.

pnpm newsletter:create -- \
  --input monthly-newsletter.json \
  --project "$GCP_PROJECT_ID"

The command creates a Plunk draft by default. Review the audience, content, and schedule in Plunk.

If the draft is correct, send it now.

pnpm newsletter:create -- \
  --input monthly-newsletter.json \
  --project "$GCP_PROJECT_ID" \
  --send

If you need a future send time, add --scheduled-at. Use an ISO 8601 time and --send.

pnpm newsletter:create -- \
  --input monthly-newsletter.json \
  --project "$GCP_PROJECT_ID" \
  --send \
  --scheduled-at 2026-10-01T14:00:00Z

Agent workflow

Give an agent the JSON field list in this section. Ask the agent to use confirmed site content and HTTPS SciTeens URLs. Ask the agent to run the dry command first. Do not give an agent PLUNK_SECRET_KEY when it only prepares content. Give send access only to an agent that can create broadcasts.

About

The official repository of the open-source SciTeens website. Commits and issues welcome!

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages