Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
FROM node:20-bookworm

# Install Postgres 17
RUN apt-get update && \
apt-get install -y gnupg2 lsb-release wget curl && \
echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
> /etc/apt/sources.list.d/pgdg.list && \
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
apt-get update && \
apt-get install -y postgresql-17 && \
rm -rf /var/lib/apt/lists/*

# Install PostgREST
RUN ARCH=$(dpkg --print-architecture) && \
if [ "$ARCH" = "arm64" ]; then \
PGRST_URL="https://github.com/PostgREST/postgrest/releases/download/v12.2.3/postgrest-v12.2.3-ubuntu-aarch64.tar.xz"; \
else \
PGRST_URL="https://github.com/PostgREST/postgrest/releases/download/v12.2.3/postgrest-v12.2.3-linux-static-x64.tar.xz"; \
fi && \
wget -qO /tmp/postgrest.tar.xz "$PGRST_URL" && \
tar xJf /tmp/postgrest.tar.xz -C /usr/local/bin && \
rm /tmp/postgrest.tar.xz

# Install GoTrue (Supabase Auth)
RUN ARCH=$(dpkg --print-architecture) && \
if [ "$ARCH" = "arm64" ]; then \
GOTRUE_URL="https://github.com/supabase/auth/releases/download/v2.186.0/auth-v2.186.0-arm64.tar.gz"; \
else \
GOTRUE_URL="https://github.com/supabase/auth/releases/download/v2.186.0/auth-v2.186.0-x86.tar.gz"; \
fi && \
wget -qO /tmp/gotrue.tar.gz "$GOTRUE_URL" && \
tar xzf /tmp/gotrue.tar.gz -C /usr/local/bin auth && \
rm /tmp/gotrue.tar.gz

# Allow postgres to run without being the postgres user
RUN sed -i "s/peer/trust/g" /etc/postgresql/17/main/pg_hba.conf && \
sed -i "s/scram-sha-256/trust/g" /etc/postgresql/17/main/pg_hba.conf && \
sed -i "s/#listen_addresses.*/listen_addresses = '*'/" /etc/postgresql/17/main/postgresql.conf && \
chown -R postgres:postgres /var/run/postgresql

WORKDIR /app

# Install dependencies first (layer caching)
COPY package.json package-lock.json ./
RUN npm ci

# Copy source
COPY . .

RUN chmod +x scripts/dev-entrypoint.sh && \
chown -R postgres:postgres /app

USER postgres
EXPOSE 3000
ENTRYPOINT ["scripts/dev-entrypoint.sh"]
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: test test-up test-down
.PHONY: test test-up test-down dev dev-down

# Run all tests or a specific test file/pattern in Docker
# Usage:
Expand All @@ -19,3 +19,12 @@ test-up:
# Remove test containers and images
test-down:
docker compose -f docker-compose.test.yml down --rmi local --volumes

# Start dev environment (Postgres + Auth + Next.js)
# App: http://localhost:3456 | Login: evrhet@postimp.com / password123
dev:
docker compose -f docker-compose.dev.yml up --build

# Stop dev environment
dev-down:
docker compose -f docker-compose.dev.yml down --volumes
17 changes: 17 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
services:
dev:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3456:3000"
- "54399:54321"
volumes:
- ./src:/app/src
- ./supabase:/app/supabase
- ./scripts:/app/scripts
tty: true
stdin_open: true
# Safety: all env vars inside the container use fake API keys/secrets.
# The container has no production credentials, so even with network access
# it cannot affect production systems.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"test:docker": "docker compose -f docker-compose.test.yml up --build --abort-on-container-exit",
"dev:docker": "docker compose -f docker-compose.dev.yml up --build",
"dev:docker:down": "docker compose -f docker-compose.dev.yml down --volumes",
"format": "biome format --write .",
"format:check": "biome format .",
"prepare": "husky",
Expand All @@ -33,6 +35,7 @@
"@zxcvbn-ts/language-common": "^3.0.4",
"@zxcvbn-ts/language-en": "^3.0.2",
"daisyui": "^5.5.19",
"jose": "^6.2.1",
"next": "^15.5.12",
"openai": "^6.25.0",
"react": "^18.3.1",
Expand Down
16 changes: 8 additions & 8 deletions scripts/backfill-permalinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async function main() {
// Find published posts with an instagram_post_id but no permalink
const { data: posts, error } = await db
.from("posts")
.select("id, profile_id, instagram_post_id")
.select("id, profile_id, organization_id, instagram_post_id")
.eq("status", "published")
.not("instagram_post_id", "is", null)
.is("instagram_permalink", null);
Expand All @@ -41,25 +41,25 @@ async function main() {

console.log(`Found ${posts.length} post(s) to backfill.\n`);

// Get unique profile IDs and fetch their access tokens
const profileIds = [...new Set(posts.map((p) => p.profile_id))];
// Get unique org IDs from posts and fetch their access tokens
const orgIds = [...new Set(posts.map((p) => p.organization_id).filter(Boolean))];
const { data: connections } = await db
.from("instagram_connections")
.select("profile_id, access_token")
.in("profile_id", profileIds);
.select("organization_id, access_token")
.in("organization_id", orgIds);

const tokenMap = new Map<string, string>();
for (const conn of connections || []) {
tokenMap.set(conn.profile_id, conn.access_token);
tokenMap.set(conn.organization_id, conn.access_token);
}

let updated = 0;
let failed = 0;

for (const post of posts) {
const accessToken = tokenMap.get(post.profile_id);
const accessToken = post.organization_id ? tokenMap.get(post.organization_id) : null;
if (!accessToken) {
console.log(` SKIP ${post.id} — no Instagram connection for profile ${post.profile_id}`);
console.log(` SKIP ${post.id} — no Instagram connection for org ${post.organization_id}`);
failed++;
continue;
}
Expand Down
Loading
Loading