Skip to content
Open
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
2 changes: 1 addition & 1 deletion .env.local.template
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,4 @@ CORS_ALLOWED_ORIGINS=http://localhost:3000,https://crowdup.io

# [AUTO] Node environment (set automatically by Next.js)
# Do not set manually unless testing
# NODE_ENV=development
# NODE_ENV=development
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ default_language_version:
repos:
# Standard file checks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: trailing-whitespace
exclude: '\.snap$'
Expand Down Expand Up @@ -38,7 +38,7 @@ repos:

# ESLint
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v9.17.0
rev: v10.7.0
hooks:
- id: eslint
name: ESLint
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ This License is governed by the laws of Italy, without regard to its conflict of

If You wish to use the Software for any purpose beyond those explicitly permitted in Section 3, You must obtain a separate written commercial license from Licensor.

**Contact for licensing inquiries:**
**Contact for licensing inquiries:**
📧 Email: <crowdupofficial@gmail.com>

---
Expand Down
6 changes: 3 additions & 3 deletions migration-companies-apps.sql
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ CREATE INDEX IF NOT EXISTS idx_company_members_company ON company_members(compan
CREATE INDEX IF NOT EXISTS idx_company_members_user ON company_members(user_id);

-- Add trigger for companies updated_at
CREATE TRIGGER update_companies_updated_at
BEFORE UPDATE ON companies
FOR EACH ROW
CREATE TRIGGER update_companies_updated_at
BEFORE UPDATE ON companies
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();

-- Insert popular companies with logos
Expand Down
2 changes: 1 addition & 1 deletion migration-follows.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ CREATE TABLE IF NOT EXISTS public.company_follows (
UNIQUE(user_id, company_id)
);

-- App follows table
-- App follows table
CREATE TABLE IF NOT EXISTS public.app_follows (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
Expand Down
14 changes: 7 additions & 7 deletions migration-improvements.sql
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ ALTER TABLE companies ADD COLUMN IF NOT EXISTS is_verified BOOLEAN DEFAULT false
ALTER TABLE companies ADD COLUMN IF NOT EXISTS post_count INTEGER DEFAULT 0;

-- Make display_name nullable or set default if it exists
DO $$
DO $$
BEGIN
-- Try to alter the column to allow nulls or have a default
ALTER TABLE companies ALTER COLUMN display_name DROP NOT NULL;
Expand All @@ -117,7 +117,7 @@ INSERT INTO companies (name, display_name, description, logo_url, website, color
('Microsoft', 'Microsoft', 'Empowering everyone', 'https://upload.wikimedia.org/wikipedia/commons/4/44/Microsoft_logo.svg', 'https://microsoft.com', '#00A4EF', 'Technology', true),
('Tesla', 'Tesla', 'Accelerating sustainable energy', 'https://upload.wikimedia.org/wikipedia/commons/e/e8/Tesla_logo.png', 'https://tesla.com', '#CC0000', 'Automotive', true),
('Stripe', 'Stripe', 'Payments infrastructure', 'https://upload.wikimedia.org/wikipedia/commons/b/ba/Stripe_Logo%2C_revised_2016.svg', 'https://stripe.com', '#635BFF', 'Fintech', true)
ON CONFLICT (name) DO UPDATE SET
ON CONFLICT (name) DO UPDATE SET
display_name = EXCLUDED.display_name,
description = EXCLUDED.description,
color = EXCLUDED.color,
Expand Down Expand Up @@ -178,7 +178,7 @@ DECLARE
BEGIN
-- Get post owner
SELECT user_id INTO post_owner_id FROM posts WHERE id = NEW.post_id;

-- Notify post owner if commenter is different
IF post_owner_id IS NOT NULL AND post_owner_id != NEW.user_id THEN
INSERT INTO notifications (user_id, type, title, message, link, actor_id, post_id)
Expand All @@ -192,7 +192,7 @@ BEGIN
NEW.post_id
);
END IF;

-- If this is a reply, notify parent comment owner
IF NEW.parent_id IS NOT NULL THEN
SELECT user_id INTO parent_comment_owner_id FROM comments WHERE id = NEW.parent_id;
Expand All @@ -209,7 +209,7 @@ BEGIN
);
END IF;
END IF;

RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Expand All @@ -226,7 +226,7 @@ DECLARE
post_owner_id UUID;
BEGIN
SELECT user_id INTO post_owner_id FROM posts WHERE id = NEW.post_id;

-- Only notify on upvotes and if voter is different from post owner
IF NEW.vote_type = 'up' AND post_owner_id IS NOT NULL AND post_owner_id != NEW.user_id THEN
INSERT INTO notifications (user_id, type, title, message, link, actor_id, post_id)
Expand All @@ -240,7 +240,7 @@ BEGIN
NEW.post_id
);
END IF;

RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Expand Down
2 changes: 1 addition & 1 deletion migration-refresh-tokens.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS refresh_tokens (
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
CONSTRAINT fk_user
FOREIGN KEY (user_id)
FOREIGN KEY (user_id)
REFERENCES users(id)
ON DELETE CASCADE
);
Expand Down
20 changes: 10 additions & 10 deletions migration-reputation-hardening.sql
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ DECLARE
BEGIN
-- Get post author
SELECT user_id INTO v_post_author_id FROM posts WHERE id = p_post_id;

IF v_post_author_id IS NULL THEN
RETURN json_build_object('success', false, 'error', 'Post not found');
END IF;
Expand All @@ -44,13 +44,13 @@ BEGIN
DELETE FROM votes WHERE post_id = p_post_id AND user_id = p_user_id;
UPDATE posts SET votes = votes + (CASE WHEN p_previous_vote = 'up' THEN -1 ELSE 1 END)
WHERE id = p_post_id RETURNING votes INTO v_new_votes;

IF v_post_author_id != p_user_id THEN
v_points_change := CASE WHEN p_previous_vote = 'up' THEN -2 ELSE 1 END;
UPDATE users SET reputation_score = GREATEST(0, reputation_score + v_points_change)
WHERE id = v_post_author_id;
END IF;

RETURN json_build_object('success', true, 'votes', v_new_votes);
END IF;

Expand All @@ -60,11 +60,11 @@ BEGIN
ON CONFLICT (post_id, user_id) DO UPDATE SET vote_type = p_vote_type;

IF p_previous_vote IS NULL THEN
v_new_votes := (SELECT votes FROM posts WHERE id = p_post_id) +
v_new_votes := (SELECT votes FROM posts WHERE id = p_post_id) +
(CASE WHEN p_vote_type = 'up' THEN 1 ELSE -1 END);
v_points_change := CASE WHEN p_vote_type = 'up' THEN 2 ELSE -1 END;
ELSE
v_new_votes := (SELECT votes FROM posts WHERE id = p_post_id) +
v_new_votes := (SELECT votes FROM posts WHERE id = p_post_id) +
(CASE WHEN p_vote_type = 'up' THEN 2 ELSE -2 END);
v_points_change := CASE WHEN p_vote_type = 'up' THEN 3 ELSE -3 END;
END IF;
Expand All @@ -74,9 +74,9 @@ BEGIN
IF v_post_author_id != p_user_id THEN
UPDATE users SET reputation_score = GREATEST(0, reputation_score + v_points_change)
WHERE id = v_post_author_id;

INSERT INTO reputation_history (user_id, action_type, points_change, related_post_id)
VALUES (v_post_author_id,
VALUES (v_post_author_id,
CASE WHEN p_vote_type = 'up' THEN 'post_upvoted' ELSE 'post_downvoted' END,
CASE WHEN p_vote_type = 'up' THEN 2 ELSE -1 END,
p_post_id);
Expand All @@ -98,10 +98,10 @@ DECLARE
BEGIN
INSERT INTO reputation_rate_limits (user_id, action_type, action_date, action_count)
VALUES (p_user_id, p_action_type, CURRENT_DATE, 1)
ON CONFLICT (user_id, action_type, action_date)
ON CONFLICT (user_id, action_type, action_date)
DO UPDATE SET action_count = reputation_rate_limits.action_count + 1
RETURNING action_count INTO v_count;

RETURN v_count <= p_daily_limit;
END;
$$ LANGUAGE plpgsql;
$$ LANGUAGE plpgsql;
24 changes: 12 additions & 12 deletions migration-security-fixes.sql
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ CREATE POLICY company_members_insert ON company_members
DROP POLICY IF EXISTS company_members_update ON company_members;
CREATE POLICY company_members_update ON company_members
FOR UPDATE USING (
auth.uid()::text = user_id::text
auth.uid()::text = user_id::text
OR EXISTS (SELECT 1 FROM users WHERE id::text = auth.uid()::text AND is_admin = true)
);

-- Policy: Only admins can delete memberships (or self-removal)
DROP POLICY IF EXISTS company_members_delete ON company_members;
CREATE POLICY company_members_delete ON company_members
FOR DELETE USING (
auth.uid()::text = user_id::text
auth.uid()::text = user_id::text
OR EXISTS (SELECT 1 FROM users WHERE id::text = auth.uid()::text AND is_admin = true)
);

Expand All @@ -92,26 +92,26 @@ DECLARE
BEGIN
-- Get post author
SELECT user_id INTO v_post_author_id FROM posts WHERE id = p_post_id;

IF v_post_author_id IS NULL THEN
RETURN json_build_object('success', false, 'error', 'Post not found');
END IF;

-- Handle vote removal
IF p_vote_type IS NULL THEN
DELETE FROM votes WHERE post_id = p_post_id AND user_id = p_user_id;

-- Update post votes
UPDATE posts SET votes = votes + (CASE WHEN p_previous_vote = 'up' THEN -1 ELSE 1 END)
WHERE id = p_post_id RETURNING votes INTO v_new_votes;

-- Reverse reputation (only if not self-vote)
IF v_post_author_id != p_user_id THEN
v_points_change := CASE WHEN p_previous_vote = 'up' THEN -2 ELSE 1 END;
UPDATE users SET reputation_score = GREATEST(0, reputation_score + v_points_change)
WHERE id = v_post_author_id;
END IF;

RETURN json_build_object('success', true, 'votes', v_new_votes);
END IF;

Expand All @@ -122,11 +122,11 @@ BEGIN

-- Calculate vote change
IF p_previous_vote IS NULL THEN
v_new_votes := (SELECT votes FROM posts WHERE id = p_post_id) +
v_new_votes := (SELECT votes FROM posts WHERE id = p_post_id) +
(CASE WHEN p_vote_type = 'up' THEN 1 ELSE -1 END);
v_points_change := CASE WHEN p_vote_type = 'up' THEN 2 ELSE -1 END;
ELSE
v_new_votes := (SELECT votes FROM posts WHERE id = p_post_id) +
v_new_votes := (SELECT votes FROM posts WHERE id = p_post_id) +
(CASE WHEN p_vote_type = 'up' THEN 2 ELSE -2 END);
v_points_change := CASE WHEN p_vote_type = 'up' THEN 3 ELSE -3 END; -- reverse old + apply new
END IF;
Expand All @@ -138,10 +138,10 @@ BEGIN
IF v_post_author_id != p_user_id THEN
UPDATE users SET reputation_score = GREATEST(0, reputation_score + v_points_change)
WHERE id = v_post_author_id;

-- Record history
INSERT INTO reputation_history (user_id, action_type, points_change, related_post_id)
VALUES (v_post_author_id,
VALUES (v_post_author_id,
CASE WHEN p_vote_type = 'up' THEN 'post_upvoted' ELSE 'post_downvoted' END,
CASE WHEN p_vote_type = 'up' THEN 2 ELSE -1 END,
p_post_id);
Expand All @@ -166,10 +166,10 @@ BEGIN
-- Get or create today's count
INSERT INTO reputation_rate_limits (user_id, action_type, action_date, action_count)
VALUES (p_user_id, p_action_type, CURRENT_DATE, 1)
ON CONFLICT (user_id, action_type, action_date)
ON CONFLICT (user_id, action_type, action_date)
DO UPDATE SET action_count = reputation_rate_limits.action_count + 1
RETURNING action_count INTO v_count;

RETURN v_count <= p_daily_limit;
END;
$$ LANGUAGE plpgsql;
20 changes: 10 additions & 10 deletions migration-update.sql
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ALTER TABLE posts ADD COLUMN IF NOT EXISTS app_id UUID;

-- Update posts type constraint to include new type
ALTER TABLE posts DROP CONSTRAINT IF EXISTS posts_type_check;
ALTER TABLE posts ADD CONSTRAINT posts_type_check
ALTER TABLE posts ADD CONSTRAINT posts_type_check
CHECK (type IN ('Bug Report', 'Feature Request', 'Complaint', 'App Review Request'));

-- Create connections/follow table
Expand Down Expand Up @@ -46,13 +46,13 @@ CREATE TABLE IF NOT EXISTS app_reviews (
);

-- Add foreign key for app_id in posts (if not exists)
DO $$
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name = 'posts_app_id_fkey'
) THEN
ALTER TABLE posts ADD CONSTRAINT posts_app_id_fkey
ALTER TABLE posts ADD CONSTRAINT posts_app_id_fkey
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE SET NULL;
END IF;
END $$;
Expand All @@ -67,12 +67,12 @@ CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
CREATE INDEX IF NOT EXISTS idx_users_display_name ON users(display_name);

-- Create triggers for new tables
CREATE TRIGGER update_apps_updated_at
BEFORE UPDATE ON apps
FOR EACH ROW
CREATE TRIGGER update_apps_updated_at
BEFORE UPDATE ON apps
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_app_reviews_updated_at
BEFORE UPDATE ON app_reviews
FOR EACH ROW
CREATE TRIGGER update_app_reviews_updated_at
BEFORE UPDATE ON app_reviews
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
6 changes: 3 additions & 3 deletions migration-verification-security.sql
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ CREATE POLICY company_members_insert ON company_members
DROP POLICY IF EXISTS company_members_update ON company_members;
CREATE POLICY company_members_update ON company_members
FOR UPDATE USING (
auth.uid()::text = user_id::text
auth.uid()::text = user_id::text
OR EXISTS (SELECT 1 FROM users WHERE id::text = auth.uid()::text AND is_admin = true)
);

DROP POLICY IF EXISTS company_members_delete ON company_members;
CREATE POLICY company_members_delete ON company_members
FOR DELETE USING (
auth.uid()::text = user_id::text
auth.uid()::text = user_id::text
OR EXISTS (SELECT 1 FROM users WHERE id::text = auth.uid()::text AND is_admin = true)
);
);
6 changes: 3 additions & 3 deletions migration-verification.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

-- Add verification columns to company_members
ALTER TABLE company_members ADD COLUMN IF NOT EXISTS verified BOOLEAN DEFAULT FALSE;
ALTER TABLE company_members ADD COLUMN IF NOT EXISTS verification_status TEXT
ALTER TABLE company_members ADD COLUMN IF NOT EXISTS verification_status TEXT
CHECK (verification_status IN ('pending', 'approved', 'rejected'));
ALTER TABLE company_members ADD COLUMN IF NOT EXISTS verification_date TIMESTAMPTZ;
ALTER TABLE company_members ADD COLUMN IF NOT EXISTS verification_documents JSONB;
Expand All @@ -13,11 +13,11 @@ ALTER TABLE company_members ADD COLUMN IF NOT EXISTS verification_notes TEXT;
ALTER TABLE users ADD COLUMN IF NOT EXISTS is_admin BOOLEAN DEFAULT FALSE;

-- Create index for pending verifications (for admin dashboard performance)
CREATE INDEX IF NOT EXISTS idx_company_members_verification_pending
CREATE INDEX IF NOT EXISTS idx_company_members_verification_pending
ON company_members(verification_status) WHERE verification_status = 'pending';

-- Create index for verified members (for quick lookup)
CREATE INDEX IF NOT EXISTS idx_company_members_verified
CREATE INDEX IF NOT EXISTS idx_company_members_verified
ON company_members(verified) WHERE verified = TRUE;

-- Create a trigger to update verification_date when status changes to approved
Expand Down
2 changes: 1 addition & 1 deletion public/file.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion public/globe.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion public/next.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading