Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixing isSubscribed bug #15

Merged
merged 3 commits into from
Dec 21, 2024
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
4 changes: 2 additions & 2 deletions database/applied/prod/post.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
CREATE TABLE posts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY NOT NULL,
text TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC') NOT NULL,
created_by UUID NOT NULL,
CONSTRAINT fk_created_by FOREIGN KEY (created_by) REFERENCES users (id) ON DELETE SET NULL
);

ALTER TABLE posts ADD COLUMN updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
ALTER TABLE posts ADD COLUMN updated_at TIMESTAMP DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC') NOT NULL;

-- Create the trigger that calls the above function before any UPDATE
CREATE TRIGGER trigger_set_updated_at
Expand Down
15 changes: 11 additions & 4 deletions database/applied/prod/subscription.sql
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
CREATE TABLE subscriptions (
id SERIAL PRIMARY KEY,
stripe_customer_id TEXT NOT NULL UNIQUE PRIMARY KEY,
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
stripe_customer_id TEXT NOT NULL UNIQUE,
subscription_id TEXT NOT NULL UNIQUE,
subscription_status TEXT NOT NULL DEFAULT 'inactive',
subscription_end_date INTEGER,
Expand Down Expand Up @@ -47,11 +46,19 @@ CREATE TABLE subscriptions (
cancel_at_period_end BOOLEAN NOT NULL DEFAULT FALSE,
amount NUMERIC,
currency TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
created_at TIMESTAMP NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'),
updated_at TIMESTAMP NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC')
);

-- Create a trigger to automatically set the updated_at column
CREATE TRIGGER trigger_set_updated_at
BEFORE UPDATE ON subscriptions
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();

-- Create a trigger to set the is_subscribed column in the users table when changed to active
CREATE TRIGGER trigger_set_is_subscribed
AFTER UPDATE OF subscription_status ON subscriptions
FOR EACH ROW
EXECUTE FUNCTION set_is_subscribed();

23 changes: 23 additions & 0 deletions database/applied/prod/triggers.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- Create a trigger function to automatically update the `updated_at` column
CREATE OR REPLACE FUNCTION set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP AT TIME ZONE 'UTC';
RETURN NEW;
END;
$$ LANGUAGE plpgsql;


-- Create a trigger function to automatically update isSubscribed column based on the subscription status
-- This trigger is applied to the subscriptions table
CREATE OR REPLACE FUNCTION set_is_subscribed()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.subscription_status = 'active' THEN
UPDATE users SET is_subscribed = TRUE where users.id = New.user_id;
ELSE
UPDATE users SET is_subscribed = FALSE where users.id = New.user_id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
13 changes: 2 additions & 11 deletions database/applied/prod/user.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,10 @@ CREATE TABLE users (
email TEXT,
handle TEXT NOT NULL,
is_subscribed BOOLEAN DEFAULT FALSE,
mohit2152sharma marked this conversation as resolved.
Show resolved Hide resolved
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
created_at TIMESTAMP NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'),
updated_at TIMESTAMP NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC')
);

-- Create a trigger function to automatically update the `updated_at` column
CREATE OR REPLACE FUNCTION set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Create the trigger that calls the above function before any UPDATE
CREATE TRIGGER trigger_set_updated_at
BEFORE UPDATE ON users
Expand Down
8 changes: 8 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@sveltejs/adapter-node": "^5.2.9",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"@types/gtag.js": "^0.0.20",
"@vitest/coverage-istanbul": "^2.1.8",
"autoprefixer": "^10.4.20",
"bits-ui": "^1.0.0-next.74",
Expand Down
2 changes: 1 addition & 1 deletion src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<link rel="icon" href="%sveltekit.assets%/edit.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
Expand Down
2 changes: 1 addition & 1 deletion src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const skipLoginCheckUrls = ['/login', '/webhooks/stripe'];
const handleAuth: Handle = async ({ event, resolve }) => {
const pathname = event.url.pathname;
const sessionToken = auth.getSessionTokenCookie(event);
if (skipLoginCheckUrls.some((url) => pathname.startsWith(url)) || pathname === "/") {
if (skipLoginCheckUrls.some((url) => pathname.startsWith(url)) || pathname === '/') {
return resolve(event);
}
if (!sessionToken) {
Expand Down
28 changes: 28 additions & 0 deletions src/lib/components/custom-comps/analytics.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script lang="ts">
import { page } from '$app/stores'
export let measurementId: string

$: {
if (typeof gtag !== 'undefined') {
gtag('config', measurementId, {
page_title: document.title,
page_path: $page.url.pathname,
})
}
}
</script>
mohit2152sharma marked this conversation as resolved.
Show resolved Hide resolved

<svelte:head>
<script async src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}>
</script>
<script>
window.dataLayer = window.dataLayer || []

function gtag() {
dataLayer.push(arguments)
}

gtag('js', new Date())
gtag('config', measurementId)
</script>
mohit2152sharma marked this conversation as resolved.
Show resolved Hide resolved
</svelte:head>
1 change: 0 additions & 1 deletion src/lib/components/custom-comps/avatar-popover.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts">
import Avatar from './avatar.svelte';
import * as Tooltip from '$lib/components/ui/tooltip';
// import { PUBLIC_STRIPE_CONSOLE_URL } from '$env/static/public';
import { env } from '$env/dynamic/public';
import { createBskyProfileUrl } from '$lib/lib-utils';

Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/custom-comps/avatar.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import * as Avatar from '$lib/components/ui/avatar/index.js';
export let userName: string = 'BS';
let avatar: string
let avatar: string;

if (userName.length > 2) {
avatar = userName.slice(0, 2).toUpperCase();
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/custom-comps/loading-button.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
export let type: 'button' | 'submit' | 'reset' | null | undefined = 'submit';
export let cls: string = '';
// export let variant: string = 'default';
export let variant: ButtonVariant = 'default'
export let variant: ButtonVariant = 'default';
export let animation: string = 'animate-spin';
export let buttonTitle: string = 'Login';
</script>
Expand Down
71 changes: 41 additions & 30 deletions src/lib/components/login-form.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<script lang="ts">
import * as Card from '$lib/components/ui/card/index';
import { Input } from '$lib/components/ui/input/index';
import { Label } from '$lib/components/ui/label/index';
import { superForm } from 'sveltekit-superforms';
import * as Form from '$lib/components/ui/form';
import type { PageData } from '../../routes/$types';
import type { LoginCredentialsSchemaType } from '$lib/server/bsky/types';
import LoadingButton from './custom-comps/loading-button.svelte';

export let data: PageData & { form: LoginCredentialsSchemaType };
const { form, submitting, enhance } = superForm(data.form);
const form = superForm(data.form);
const { form: formData, submitting, enhance } = form;
</script>

<Card.Root class="mx-auto max-w-sm">
Expand All @@ -19,35 +20,45 @@
<Card.Content>
<form method="POST" use:enhance action="?/login">
<div class="grid gap-4">
<Form.Field {form} name="identifier">
<Form.Control>
<div class="grid gap-2">
<Form.Label for="identifier">Bluesky Handle</Form.Label>
<Input
id="identifier"
name="identifier"
placeholder="user.bsky.social"
bind:value={$formData.identifier}
required
/>
</div>
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<div class="grid gap-2">
<Label for="identifier">Bluesky Handle</Label>
<Input
id="identifier"
name="identifier"
placeholder="user.bsky.social"
bind:value={$form.identifier}
required
/>
</div>
<div class="grid gap-2">
<div class="flex items-center">
<Label for="password">App Password</Label>
<a
href="https://blueskyfeeds.com/en/faq-app-password"
class="ml-auto inline-block text-sm underline"
target="_blank"
rel="noreferrer"
>
App Password?
</a>
</div>
<Input
id="password"
bind:value={$form.password}
type="password"
name="password"
required
/>
<Form.Field {form} name="password">
<Form.Control>
<div class="flex items-center">
<Form.Label for="password">App Password</Form.Label>
<a
href="https://blueskyfeeds.com/en/faq-app-password"
class="ml-auto inline-block text-sm underline"
target="_blank"
rel="noreferrer"
>
App Password?
</a>
</div>
<Input
id="password"
bind:value={$formData.password}
type="password"
name="password"
required
/>
</Form.Control>
<Form.FieldErrors />
</Form.Field>
</div>
<LoadingButton submitting={$submitting} cls="w-full" />
</div>
Expand Down
1 change: 1 addition & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const MAX_IMAGE_SIZE = 1000000;
export const PDS_URL = 'https://bsky.social';
export const CHARACTER_LIMIT = 300;
export const GOOGLE_MEASUREMENT_ID = 'G-P0YEWW1Z8J'
8 changes: 6 additions & 2 deletions src/lib/lib-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { RequestEvent } from '@sveltejs/kit';
import { Logger } from './logger';
// import { PUBLIC_MY_ENV, PUBLIC_BSKY_POST_SUCCESS } from '$env/static/public';
import { env } from '$env/dynamic/public';
import fs from 'fs/promises';

Expand Down Expand Up @@ -32,6 +31,10 @@ function isEnvDev() {
return ['development', 'test', 'dev'].includes(env.PUBLIC_MY_ENV);
}

function isEnvProd() {
return ['production', 'prod'].includes(env.PUBLIC_MY_ENV);
}

function isEnvDevAndPostSuccess() {
return isEnvDev() && checkBoolean(env.PUBLIC_BSKY_POST_SUCCESS);
}
Expand Down Expand Up @@ -129,5 +132,6 @@ export {
createBskyProfileUrl,
isEnvDev,
isEnvDevAndPostSuccess,
checkBoolean
checkBoolean,
isEnvProd
};
3 changes: 2 additions & 1 deletion src/lib/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ export class Logger {

getLogLevel(level?: string | LogLevel) {
if (!level) {
// check if LOG_LEVEL is set in environment
// TODO: vite throws warning Module "path" has been externalized for browser
// Avoid nodejs modules to reduce bundle size
level = typeof window === 'undefined' ? env.PUBLIC_LOG_LEVEL : env.PUBLIC_LOG_LEVEL;
if (!level) {
return LogLevel.INFO;
Expand Down
18 changes: 7 additions & 11 deletions src/lib/server/db/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,11 @@ export const upsertSubscriber = async (
.onConflictDoUpdate({ set: subscriber, target: subscriptions.stripeCustomerId });
};

export const checkAndInsertNewSubscriber = async (
subscriber: SubscriptionInsert
): Promise<Array<SubscriptionSelect>> => {
const existingSubscriber = await getSubscriber({ stripeCustomerId: subscriber.stripeCustomerId });
if (existingSubscriber.length === 0) {
logger.info("Subscriber doesn't exist in db inserting");
return await insertNewSubscriber(subscriber);
} else {
logger.info('Subscriber already exists');
}
return existingSubscriber;
export const checkSubscription = async (userId: string): Promise<boolean> => {
const subscription = await getSubscriber({ userId });
return subscription.length === 0
? false
: subscription[0].subscriptionStatus === 'active'
? true
: false;
};
3 changes: 3 additions & 0 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<script lang="ts">
import '../app.css';
let { children } = $props();
import Analytics from '$lib/components/custom-comps/analytics.svelte';
import { GOOGLE_MEASUREMENT_ID } from '$lib/constants';
Comment on lines +4 to +5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Privacy and Consent Mechanisms Required for Analytics Implementation

The current Google Analytics implementation lacks essential privacy features:

  • No Do-Not-Track (DNT) detection and respect mechanism
  • Missing cookie consent implementation before loading analytics
  • No privacy notice or opt-out functionality
  • The analytics script loads unconditionally without user consent

Required changes:

  • Add cookie consent mechanism before loading the analytics component
  • Implement DNT header detection
  • Add privacy notice and user opt-out functionality
  • Consider making the analytics script load conditionally based on user consent
🔗 Analysis chain

Verify Google Analytics configuration and privacy compliance

The Analytics integration looks correct, but please ensure:

  1. The measurement ID is properly configured in your Google Analytics account
  2. You have appropriate privacy notices and cookie consent mechanisms in place
  3. The analytics respect Do-Not-Track settings

Also applies to: 8-8


Let me search for privacy-related implementations and analytics configuration using more generic patterns, as the previous command had issues with the file type recognition. This will help us verify the privacy compliance concerns raised in the review comment.


Let me examine the actual implementation of the analytics component and auth settings to verify if there are any privacy-related mechanisms in place.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for privacy policy and cookie consent implementations
rg -l "cookie|privacy|consent|gdpr" --type svelte

Length of output: 82


Script:

#!/bin/bash
# Check for privacy policy and cookie consent implementations with generic file search
rg -l "cookie|privacy|consent|gdpr"

# Also check for analytics-related configuration and privacy settings
rg -l "analytics|tracking|measurement|opt.*out"

# Look for the measurement ID constant
rg "GOOGLE_MEASUREMENT_ID"

Length of output: 577


Script:

#!/bin/bash
# Check the analytics component implementation for privacy features
cat src/lib/components/custom-comps/analytics.svelte

# Check auth implementation for any privacy-related settings
cat src/lib/server/auth.ts

Length of output: 1918

</script>

<Analytics measurementId={GOOGLE_MEASUREMENT_ID} />
{@render children()}
Loading
Loading