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
8 changes: 4 additions & 4 deletions .pre-commit-ci.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Configuration for https://pre-commit.ci/
ci:
autofix_prs: true
autofix_commit_msg: '[pre-commit.ci] auto fixes from hooks'
autofix_commit_msg: "[pre-commit.ci] auto fixes from hooks"
autoupdate_schedule: weekly
autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate'
autoupdate_branch: ''
skip: [] # Add hook IDs to skip on CI if needed: e.g., [eslint]
autoupdate_commit_msg: "[pre-commit.ci] pre-commit autoupdate"
autoupdate_branch: ""
skip: [] # Add hook IDs to skip on CI if needed: e.g., [eslint]
submodules: false
19 changes: 10 additions & 9 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,21 @@ repos:
exclude: '\.snap$'
- id: end-of-file-fixer
- id: check-yaml
args: ['--unsafe'] # Allow custom tags (GitHub Actions)
args: ["--unsafe"] # Allow custom tags (GitHub Actions)
- id: check-json
- id: check-merge-conflict
- id: check-added-large-files
args: ['--maxkb=500']
args: ["--maxkb=500"]

# Prettier (using local hook - mirror archived in April 2024)
# Prettier
- repo: local
hooks:
- id: prettier
name: Prettier
entry: npx prettier --write --ignore-unknown
entry: prettier --write --ignore-unknown
language: node
types: [text]
additional_dependencies: ["prettier@3.7.4"]
exclude: |
(?x)^(
package-lock\.json|
Expand All @@ -44,11 +45,11 @@ repos:
name: ESLint
files: \.[jt]sx?$
types: [file]
args: ['--fix']
args: ["--fix"]
additional_dependencies:
- eslint@9.37.0
- eslint-config-next@15.5.4
- 'eslint-plugin-import@latest'
- '@eslint/eslintrc@3.3.1'
- '@typescript-eslint/eslint-plugin@8.22.0'
- '@typescript-eslint/parser@8.22.0'
- "eslint-plugin-import@latest"
- "@eslint/eslintrc@3.3.1"
- "@typescript-eslint/eslint-plugin@8.22.0"
- "@typescript-eslint/parser@8.22.0"
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)
);
);
Loading
Loading