# frontpage - https://frontpage.fyi - https://bsky.app/profile/frontpage.fyi - https://github.com/likeandscribe/frontpage ```sh $ git clone https://github.com/likeandscribe/frontpage $ dir=${0:a:h}/frontpage $ cd $dir ``` ## first setting ```sh $ cd $dir $ nvm use 20 $ pnpm i $ cat turbo.json $ pnpm exec turbo run --affected type-check ``` ```sh $ cd $dir/packages/frontpage $ pnpm exec tsx ./scripts/generate-jwk.mts # pnpm run db:generate # pnpm run db:migrate $ cat .env.local ``` ```sh # frontpage/.env.local PRIVATE_JWK=`pnpm exec tsx ./scripts/generate-jwk.mts` PUBLIC_JWK=`pnpm exec tsx ./scripts/generate-jwk.mts` TURSO_CONNECTION_URL=libsql://xxx.turso.io #TURSO_CONNECTION_URL=`turso db shell xxx-xxx` TURSO_AUTH_TOKEN=`turso db tokens create xxx-xxx` DRAINPIPE_CONSUMER_SECRET=`openssl ecparam --name secp256k1 --genkey --noout --outform DER | tail --bytes=+8 | head --bytes=32 | xxd --plain --cols 32` VERCEL_PROJECT_PRODUCTION_URL=example.com VERCEL_BRANCH_URL=example.com #DRAINPIPE_CONSUMER_SECRET=secret #TURSO_CONNECTION_URL=libsql://turso.dev.unravel.fyi #PLC_DIRECTORY_URL=https://plc.dev.unravel.fyi ``` ```sh $ cd $dir/packages-rs/drainpipe $ cargo install diesel_cli --no-default-features --features sqlite $ diesel setup $ diesel migration run $ ls drainpipe.db $ cat .env.local ``` ```sh # drainpipe/.env.local DATABASE_URL="drainpipe.db" FRONTPAGE_CONSUMER_URL="http://${cloudflared}/api/receive_hook" FRONTPAGE_CONSUMER_SECRET=`openssl ecparam --name secp256k1 --genkey --noout --outform DER | tail --bytes=+8 | head --bytes=32 | xxd --plain --cols 32` #RELAY_URL=wss://bsky.network #FRONTPAGE_CONSUMER_SECRET=secret ``` ## rewrite ```sh $ cd $dir/packages/frontpage $ PUBLIC_URL=example.com $ grep -R frontpage.fyi ./app ./lib |cut -d : -f 1|sed -i "s/frontpage.fyi/$PUBLIC_URL/g" $ HOST_REVERT=com.unravel.example $ grep -R unravel.frontpage ./app ./lib |cut -d : -f 1|xargs sed -i "s/fyi.unravel.frontpage/${HOST_REVERT}/g" ``` ```sh $ cd $dir/packages-rs/drainpipe $ HOST_REVERT=com.unravel.example $ grep -R fyi.unravel.frontpage ./src |cut -d : -f 1|xargs sed -i "s/fyi.unravel.frontpage/${HOST_REVERT}/g" ``` ## deploy ```sh $ cd $dir/packages-rs/drainpipe $ docker compose up --- $ cd $dir/packages/frontpage $ docker compose up ``` ## explanation ### client-metadata.json the `client_id` is different between `pnpm run start` and `pnpm run dev`. see `https://localhost:3000/oauth/client-metadata.json` for this. ### local-infra i think this is the server configuration required for self-hosting. https://github.com/likeandscribe/frontpage/tree/main/packages/frontpage/local-infra since plc gives an error, do the following. probably a postgres database is required. there is no need to open ports. ```yml plc: image: ghcr.io/bluesky-social/did-method-plc:plc-f2ab7516bac5bc0f3f86842fa94e996bd1b3815b container_name: plc restart: unless-stopped ports: - '4000:8080' depends_on: - plc_db env_file: - ./plc.env plc_db: image: postgres:16-alpine restart: always env_file: - ./postgres.env volumes: - ./configs/postgres/init/:/docker-entrypoint-initdb.d/ - ./data/postgres/:/var/lib/postgresql/data/ healthcheck: test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB" interval: 5s retries: 20 ``` ```sh # plc.env DEBUG_MODE=1 LOG_ENABLED=true LOG_LEVEL=debug LOG_DESTINATION=1 PORT=8080 DATABASE_URL=postgres://postgres:postgres@plc_db/plc DB_CREDS_JSON='{"username":"postgres","password":"postgres","host":"plc_db","port":"5432","database":"plc"}' ENABLE_MIGRATIONS=true DB_MIGRATE_CREDS_JSON='{"username":"postgres","password":"postgres","host":"plc_db","port":"5432","database":"plc"}' ``` ```sh # configs/postgres/init/init.sql -- PLC CREATE DATABASE plc; GRANT ALL PRIVILEGES ON DATABASE plc TO postgres; ``` ### pds first, i think you need to get the pdsurl with oauth(session). if you have a session, you can perform operations such as posting. ```sh $ cd $dir/packages/frontpage ./lib/data/user.ts: const pdsUrl = await getPdsUrl(session.user.did); ``` it seems that drainpipe searches for `fyi.unravel.frontpage(collection)` in pds and commits it to firehose subscriberepos. if you change these two parts, it will not work with `frontpage.fyi`. ```rust // https://github.com/likeandscribe/frontpage/blob/e7444ec6c19f0ccef3776f04702c3bb033ed3bfc/packages-rs/drainpipe/src/main.rs#L66-L97 // RELAY_URL=wss://bsky.network let mut ws_request = format!( "{}/xrpc/com.atproto.sync.subscribeRepos{}", relay_url, query_string ) /// Process a message from the firehose. Returns the sequence number of the message or an error. async fn process(message: Vec, ctx: &mut Context) -> Result { let (_header, data) = firehose::read(&message).map_err(|e| ProcessError { inner: e.into(), seq: -1, source: message.clone().into(), kind: ProcessErrorKind::DecodeError, })?; let sequence = match data { firehose::SubscribeRepos::Commit(commit) => { let frontpage_ops = commit .operations .iter() .filter(|op| op.path.starts_with("com.unravel.example.")) //.filter(|op| op.path.starts_with("fyi.unravel.frontpage.")) .collect::>(); if !frontpage_ops.is_empty() { process_frontpage_ops(&frontpage_ops, &commit, &ctx) .map_err(|e| ProcessError { seq: commit.sequence, inner: e, source: message.clone().into(), kind: ProcessErrorKind::ProcessError, }) .await?; } commit.sequence } msg => msg.sequence(), }; Ok(sequence) } ``` ## other ### license view ```html // https://github.com/likeandscribe/frontpage/blob/de31aedf73c4e80e7376cf73c7c054437563f2ab/packages/frontpage/app/layout.tsx#L28-L52 +
'}} /> ``` ### admin view ```sh # https://github.com/likeandscribe/frontpage/blob/7fccf20fa800ba25fd57db279033ddf2cc92e9ce/packages/frontpage/lib/constants.ts ./lib/constants.ts:export const FRONTPAGE_ATPROTO_HANDLE = "admin.example.com"; # https://github.com/likeandscribe/frontpage/blob/cf8a4cb8bc7bab54407972964f8d39bf5e7c9182/packages/frontpage/app/(app)/layout.tsx#L55-L66 ./app/\(app\)/layout.tsx:@admin.example.com ```