diff --git a/.env.example b/.env.example index 36cae22..2a12a0d 100644 --- a/.env.example +++ b/.env.example @@ -10,9 +10,8 @@ PORT=4321 DATABASE_URL=sqlite://data/gitea-mirror.db # Security -JWT_SECRET=change-this-to-a-secure-random-string-in-production BETTER_AUTH_SECRET=change-this-to-a-secure-random-string-in-production -BETTER_AUTH_URL=http://localhost:3000 +BETTER_AUTH_URL=http://localhost:4321 # Optional GitHub/Gitea Mirror Configuration (for docker-compose, can also be set via web UI) # Uncomment and set as needed. These are passed as environment variables to the container. diff --git a/docker-compose.alt.yml b/docker-compose.alt.yml index ac011be..9e6a76e 100644 --- a/docker-compose.alt.yml +++ b/docker-compose.alt.yml @@ -15,7 +15,7 @@ services: - DATABASE_URL=file:data/gitea-mirror.db - HOST=0.0.0.0 - PORT=4321 - - JWT_SECRET=${JWT_SECRET:-your-secret-key-change-this-in-production} + - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-your-secret-key-change-this-in-production} healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=3", "--spider", "http://localhost:4321/api/health"] interval: 30s diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index a4824c9..321297e 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -66,7 +66,7 @@ services: - DATABASE_URL=file:data/gitea-mirror.db - HOST=0.0.0.0 - PORT=4321 - - JWT_SECRET=dev-secret-key + - BETTER_AUTH_SECRET=dev-secret-key # GitHub/Gitea Mirror Config - GITHUB_USERNAME=${GITHUB_USERNAME:-your-github-username} - GITHUB_TOKEN=${GITHUB_TOKEN:-your-github-token} diff --git a/docker-compose.yml b/docker-compose.yml index 527ca05..56373f3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,7 +28,7 @@ services: - DATABASE_URL=file:data/gitea-mirror.db - HOST=0.0.0.0 - PORT=4321 - - JWT_SECRET=${JWT_SECRET:-your-secret-key-change-this-in-production} + - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-your-secret-key-change-this-in-production} # GitHub/Gitea Mirror Config - GITHUB_USERNAME=${GITHUB_USERNAME:-} - GITHUB_TOKEN=${GITHUB_TOKEN:-} diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 2ea977d..e1b1a43 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -52,15 +52,26 @@ if [ "$GITEA_SKIP_TLS_VERIFY" = "true" ]; then export NODE_TLS_REJECT_UNAUTHORIZED=0 fi -# Generate a secure JWT secret if one isn't provided or is using the default value -JWT_SECRET_FILE="/app/data/.jwt_secret" -if [ "$JWT_SECRET" = "your-secret-key-change-this-in-production" ] || [ -z "$JWT_SECRET" ]; then +# Generate a secure BETTER_AUTH_SECRET if one isn't provided or is using the default value +BETTER_AUTH_SECRET_FILE="/app/data/.better_auth_secret" +JWT_SECRET_FILE="/app/data/.jwt_secret" # Old file for backward compatibility + +if [ "$BETTER_AUTH_SECRET" = "your-secret-key-change-this-in-production" ] || [ -z "$BETTER_AUTH_SECRET" ]; then # Check if we have a previously generated secret - if [ -f "$JWT_SECRET_FILE" ]; then - echo "Using previously generated JWT secret" - export JWT_SECRET=$(cat "$JWT_SECRET_FILE") + if [ -f "$BETTER_AUTH_SECRET_FILE" ]; then + echo "Using previously generated BETTER_AUTH_SECRET" + export BETTER_AUTH_SECRET=$(cat "$BETTER_AUTH_SECRET_FILE") + # Check for old JWT_SECRET file for backward compatibility + elif [ -f "$JWT_SECRET_FILE" ]; then + echo "Migrating from old JWT_SECRET to BETTER_AUTH_SECRET" + export BETTER_AUTH_SECRET=$(cat "$JWT_SECRET_FILE") + # Save to new file + echo "$BETTER_AUTH_SECRET" > "$BETTER_AUTH_SECRET_FILE" + chmod 600 "$BETTER_AUTH_SECRET_FILE" + # Optionally remove old file after successful migration + rm -f "$JWT_SECRET_FILE" else - echo "Generating a secure random JWT secret" + echo "Generating a secure random BETTER_AUTH_SECRET" # Try to generate a secure random string using OpenSSL if command -v openssl >/dev/null 2>&1; then GENERATED_SECRET=$(openssl rand -hex 32) @@ -69,12 +80,12 @@ if [ "$JWT_SECRET" = "your-secret-key-change-this-in-production" ] || [ -z "$JWT echo "OpenSSL not found, using fallback method for random generation" GENERATED_SECRET=$(head -c 32 /dev/urandom | sha256sum | cut -d' ' -f1) fi - export JWT_SECRET="$GENERATED_SECRET" + export BETTER_AUTH_SECRET="$GENERATED_SECRET" # Save the secret to a file for persistence across container restarts - echo "$GENERATED_SECRET" > "$JWT_SECRET_FILE" - chmod 600 "$JWT_SECRET_FILE" + echo "$GENERATED_SECRET" > "$BETTER_AUTH_SECRET_FILE" + chmod 600 "$BETTER_AUTH_SECRET_FILE" fi - echo "JWT_SECRET has been set to a secure random value" + echo "BETTER_AUTH_SECRET has been set to a secure random value" fi diff --git a/drizzle/0000_big_xorn.sql b/drizzle/0000_init.sql similarity index 70% rename from drizzle/0000_big_xorn.sql rename to drizzle/0000_init.sql index b3c6a8c..99809c7 100644 --- a/drizzle/0000_big_xorn.sql +++ b/drizzle/0000_init.sql @@ -1,3 +1,21 @@ +CREATE TABLE `accounts` ( + `id` text PRIMARY KEY NOT NULL, + `account_id` text NOT NULL, + `user_id` text NOT NULL, + `provider_id` text NOT NULL, + `provider_user_id` text, + `access_token` text, + `refresh_token` text, + `expires_at` integer, + `password` text, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE INDEX `idx_accounts_account_id` ON `accounts` (`account_id`);--> statement-breakpoint +CREATE INDEX `idx_accounts_user_id` ON `accounts` (`user_id`);--> statement-breakpoint +CREATE INDEX `idx_accounts_provider` ON `accounts` (`provider_id`,`provider_user_id`);--> statement-breakpoint CREATE TABLE `configs` ( `id` text PRIMARY KEY NOT NULL, `user_id` text NOT NULL, @@ -120,11 +138,43 @@ CREATE INDEX `idx_repositories_owner` ON `repositories` (`owner`);--> statement- CREATE INDEX `idx_repositories_organization` ON `repositories` (`organization`);--> statement-breakpoint CREATE INDEX `idx_repositories_is_fork` ON `repositories` (`is_fork`);--> statement-breakpoint CREATE INDEX `idx_repositories_is_starred` ON `repositories` (`is_starred`);--> statement-breakpoint +CREATE TABLE `sessions` ( + `id` text PRIMARY KEY NOT NULL, + `token` text NOT NULL, + `user_id` text NOT NULL, + `expires_at` integer NOT NULL, + `ip_address` text, + `user_agent` text, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE UNIQUE INDEX `sessions_token_unique` ON `sessions` (`token`);--> statement-breakpoint +CREATE INDEX `idx_sessions_user_id` ON `sessions` (`user_id`);--> statement-breakpoint +CREATE INDEX `idx_sessions_token` ON `sessions` (`token`);--> statement-breakpoint +CREATE INDEX `idx_sessions_expires_at` ON `sessions` (`expires_at`);--> statement-breakpoint CREATE TABLE `users` ( `id` text PRIMARY KEY NOT NULL, - `username` text NOT NULL, - `password` text NOT NULL, + `name` text, `email` text NOT NULL, + `email_verified` integer DEFAULT false NOT NULL, + `image` text, `created_at` integer DEFAULT (unixepoch()) NOT NULL, - `updated_at` integer DEFAULT (unixepoch()) NOT NULL + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + `username` text ); +--> statement-breakpoint +CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint +CREATE TABLE `verification_tokens` ( + `id` text PRIMARY KEY NOT NULL, + `token` text NOT NULL, + `identifier` text NOT NULL, + `type` text NOT NULL, + `expires_at` integer NOT NULL, + `created_at` integer DEFAULT (unixepoch()) NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `verification_tokens_token_unique` ON `verification_tokens` (`token`);--> statement-breakpoint +CREATE INDEX `idx_verification_tokens_token` ON `verification_tokens` (`token`);--> statement-breakpoint +CREATE INDEX `idx_verification_tokens_identifier` ON `verification_tokens` (`identifier`); \ No newline at end of file diff --git a/drizzle/0001_vengeful_whirlwind.sql b/drizzle/0001_vengeful_whirlwind.sql deleted file mode 100644 index be593b1..0000000 --- a/drizzle/0001_vengeful_whirlwind.sql +++ /dev/null @@ -1,45 +0,0 @@ -CREATE TABLE `accounts` ( - `id` text PRIMARY KEY NOT NULL, - `user_id` text NOT NULL, - `provider_id` text NOT NULL, - `provider_user_id` text NOT NULL, - `access_token` text, - `refresh_token` text, - `expires_at` integer, - `password` text, - `created_at` integer DEFAULT (unixepoch()) NOT NULL, - `updated_at` integer DEFAULT (unixepoch()) NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE INDEX `idx_accounts_user_id` ON `accounts` (`user_id`);--> statement-breakpoint -CREATE INDEX `idx_accounts_provider` ON `accounts` (`provider_id`,`provider_user_id`);--> statement-breakpoint -CREATE TABLE `sessions` ( - `id` text PRIMARY KEY NOT NULL, - `token` text NOT NULL, - `user_id` text NOT NULL, - `expires_at` integer NOT NULL, - `ip_address` text, - `user_agent` text, - `created_at` integer DEFAULT (unixepoch()) NOT NULL, - `updated_at` integer DEFAULT (unixepoch()) NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action -); ---> statement-breakpoint -CREATE UNIQUE INDEX `sessions_token_unique` ON `sessions` (`token`);--> statement-breakpoint -CREATE INDEX `idx_sessions_user_id` ON `sessions` (`user_id`);--> statement-breakpoint -CREATE INDEX `idx_sessions_token` ON `sessions` (`token`);--> statement-breakpoint -CREATE INDEX `idx_sessions_expires_at` ON `sessions` (`expires_at`);--> statement-breakpoint -CREATE TABLE `verification_tokens` ( - `id` text PRIMARY KEY NOT NULL, - `token` text NOT NULL, - `identifier` text NOT NULL, - `type` text NOT NULL, - `expires_at` integer NOT NULL, - `created_at` integer DEFAULT (unixepoch()) NOT NULL -); ---> statement-breakpoint -CREATE UNIQUE INDEX `verification_tokens_token_unique` ON `verification_tokens` (`token`);--> statement-breakpoint -CREATE INDEX `idx_verification_tokens_token` ON `verification_tokens` (`token`);--> statement-breakpoint -CREATE INDEX `idx_verification_tokens_identifier` ON `verification_tokens` (`identifier`);--> statement-breakpoint -ALTER TABLE `users` ADD `email_verified` integer DEFAULT false NOT NULL; \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json index 74f1ffa..231493d 100644 --- a/drizzle/meta/0000_snapshot.json +++ b/drizzle/meta/0000_snapshot.json @@ -1,9 +1,135 @@ { "version": "6", "dialect": "sqlite", - "id": "b963d828-412d-4192-b0aa-3b13b83cfba8", + "id": "7782b8ba-bdae-42e8-b8a7-614f8be30a58", "prevId": "00000000-0000-0000-0000-000000000000", "tables": { + "accounts": { + "name": "accounts", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "idx_accounts_account_id": { + "name": "idx_accounts_account_id", + "columns": [ + "account_id" + ], + "isUnique": false + }, + "idx_accounts_user_id": { + "name": "idx_accounts_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_accounts_provider": { + "name": "idx_accounts_provider", + "columns": [ + "provider_id", + "provider_user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, "configs": { "name": "configs", "columns": { @@ -887,8 +1013,8 @@ "uniqueConstraints": {}, "checkConstraints": {} }, - "users": { - "name": "users", + "sessions": { + "name": "sessions", "columns": { "id": { "name": "id", @@ -897,27 +1023,41 @@ "notNull": true, "autoincrement": false }, - "username": { - "name": "username", + "token": { + "name": "token", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false }, - "password": { - "name": "password", + "user_id": { + "name": "user_id", "type": "text", "primaryKey": false, "notNull": true, "autoincrement": false }, - "email": { - "name": "email", - "type": "text", + "expires_at": { + "name": "expires_at", + "type": "integer", "primaryKey": false, "notNull": true, "autoincrement": false }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, "created_at": { "name": "created_at", "type": "integer", @@ -935,7 +1075,202 @@ "default": "(unixepoch())" } }, - "indexes": {}, + "indexes": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_sessions_user_id": { + "name": "idx_sessions_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_sessions_token": { + "name": "idx_sessions_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_sessions_expires_at": { + "name": "idx_sessions_expires_at", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification_tokens": { + "name": "verification_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())" + } + }, + "indexes": { + "verification_tokens_token_unique": { + "name": "verification_tokens_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_verification_tokens_token": { + "name": "idx_verification_tokens_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_verification_tokens_identifier": { + "name": "idx_verification_tokens_identifier", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, "foreignKeys": {}, "compositePrimaryKeys": {}, "uniqueConstraints": {}, diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json deleted file mode 100644 index c636152..0000000 --- a/drizzle/meta/0001_snapshot.json +++ /dev/null @@ -1,1261 +0,0 @@ -{ - "version": "6", - "dialect": "sqlite", - "id": "590d3057-e9b0-4113-af8e-8dc2c3d8d737", - "prevId": "b963d828-412d-4192-b0aa-3b13b83cfba8", - "tables": { - "accounts": { - "name": "accounts", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "provider_id": { - "name": "provider_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "provider_user_id": { - "name": "provider_user_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "access_token": { - "name": "access_token", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "refresh_token": { - "name": "refresh_token", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "expires_at": { - "name": "expires_at", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "created_at": { - "name": "created_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - }, - "updated_at": { - "name": "updated_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - } - }, - "indexes": { - "idx_accounts_user_id": { - "name": "idx_accounts_user_id", - "columns": [ - "user_id" - ], - "isUnique": false - }, - "idx_accounts_provider": { - "name": "idx_accounts_provider", - "columns": [ - "provider_id", - "provider_user_id" - ], - "isUnique": false - } - }, - "foreignKeys": { - "accounts_user_id_users_id_fk": { - "name": "accounts_user_id_users_id_fk", - "tableFrom": "accounts", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "configs": { - "name": "configs", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "is_active": { - "name": "is_active", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": true - }, - "github_config": { - "name": "github_config", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "gitea_config": { - "name": "gitea_config", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "include": { - "name": "include", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "'[\"*\"]'" - }, - "exclude": { - "name": "exclude", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "'[]'" - }, - "schedule_config": { - "name": "schedule_config", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "cleanup_config": { - "name": "cleanup_config", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "created_at": { - "name": "created_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - }, - "updated_at": { - "name": "updated_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - } - }, - "indexes": {}, - "foreignKeys": { - "configs_user_id_users_id_fk": { - "name": "configs_user_id_users_id_fk", - "tableFrom": "configs", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "events": { - "name": "events", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "channel": { - "name": "channel", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "payload": { - "name": "payload", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "read": { - "name": "read", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": false - }, - "created_at": { - "name": "created_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - } - }, - "indexes": { - "idx_events_user_channel": { - "name": "idx_events_user_channel", - "columns": [ - "user_id", - "channel" - ], - "isUnique": false - }, - "idx_events_created_at": { - "name": "idx_events_created_at", - "columns": [ - "created_at" - ], - "isUnique": false - }, - "idx_events_read": { - "name": "idx_events_read", - "columns": [ - "read" - ], - "isUnique": false - } - }, - "foreignKeys": { - "events_user_id_users_id_fk": { - "name": "events_user_id_users_id_fk", - "tableFrom": "events", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "mirror_jobs": { - "name": "mirror_jobs", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "repository_id": { - "name": "repository_id", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "repository_name": { - "name": "repository_name", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "organization_id": { - "name": "organization_id", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "organization_name": { - "name": "organization_name", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "details": { - "name": "details", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "status": { - "name": "status", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "'imported'" - }, - "message": { - "name": "message", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "timestamp": { - "name": "timestamp", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - }, - "job_type": { - "name": "job_type", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "'mirror'" - }, - "batch_id": { - "name": "batch_id", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "total_items": { - "name": "total_items", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "completed_items": { - "name": "completed_items", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false, - "default": 0 - }, - "item_ids": { - "name": "item_ids", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "completed_item_ids": { - "name": "completed_item_ids", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false, - "default": "'[]'" - }, - "in_progress": { - "name": "in_progress", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": false - }, - "started_at": { - "name": "started_at", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "completed_at": { - "name": "completed_at", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "last_checkpoint": { - "name": "last_checkpoint", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": { - "idx_mirror_jobs_user_id": { - "name": "idx_mirror_jobs_user_id", - "columns": [ - "user_id" - ], - "isUnique": false - }, - "idx_mirror_jobs_batch_id": { - "name": "idx_mirror_jobs_batch_id", - "columns": [ - "batch_id" - ], - "isUnique": false - }, - "idx_mirror_jobs_in_progress": { - "name": "idx_mirror_jobs_in_progress", - "columns": [ - "in_progress" - ], - "isUnique": false - }, - "idx_mirror_jobs_job_type": { - "name": "idx_mirror_jobs_job_type", - "columns": [ - "job_type" - ], - "isUnique": false - }, - "idx_mirror_jobs_timestamp": { - "name": "idx_mirror_jobs_timestamp", - "columns": [ - "timestamp" - ], - "isUnique": false - } - }, - "foreignKeys": { - "mirror_jobs_user_id_users_id_fk": { - "name": "mirror_jobs_user_id_users_id_fk", - "tableFrom": "mirror_jobs", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "organizations": { - "name": "organizations", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "config_id": { - "name": "config_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "avatar_url": { - "name": "avatar_url", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "membership_role": { - "name": "membership_role", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "'member'" - }, - "is_included": { - "name": "is_included", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": true - }, - "destination_org": { - "name": "destination_org", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "status": { - "name": "status", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "'imported'" - }, - "last_mirrored": { - "name": "last_mirrored", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "error_message": { - "name": "error_message", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "repository_count": { - "name": "repository_count", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": 0 - }, - "created_at": { - "name": "created_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - }, - "updated_at": { - "name": "updated_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - } - }, - "indexes": { - "idx_organizations_user_id": { - "name": "idx_organizations_user_id", - "columns": [ - "user_id" - ], - "isUnique": false - }, - "idx_organizations_config_id": { - "name": "idx_organizations_config_id", - "columns": [ - "config_id" - ], - "isUnique": false - }, - "idx_organizations_status": { - "name": "idx_organizations_status", - "columns": [ - "status" - ], - "isUnique": false - }, - "idx_organizations_is_included": { - "name": "idx_organizations_is_included", - "columns": [ - "is_included" - ], - "isUnique": false - } - }, - "foreignKeys": { - "organizations_user_id_users_id_fk": { - "name": "organizations_user_id_users_id_fk", - "tableFrom": "organizations", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "organizations_config_id_configs_id_fk": { - "name": "organizations_config_id_configs_id_fk", - "tableFrom": "organizations", - "tableTo": "configs", - "columnsFrom": [ - "config_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "repositories": { - "name": "repositories", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "config_id": { - "name": "config_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "full_name": { - "name": "full_name", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "url": { - "name": "url", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "clone_url": { - "name": "clone_url", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "owner": { - "name": "owner", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "organization": { - "name": "organization", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "mirrored_location": { - "name": "mirrored_location", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false, - "default": "''" - }, - "is_private": { - "name": "is_private", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": false - }, - "is_fork": { - "name": "is_fork", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": false - }, - "forked_from": { - "name": "forked_from", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "has_issues": { - "name": "has_issues", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": false - }, - "is_starred": { - "name": "is_starred", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": false - }, - "is_archived": { - "name": "is_archived", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": false - }, - "size": { - "name": "size", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": 0 - }, - "has_lfs": { - "name": "has_lfs", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": false - }, - "has_submodules": { - "name": "has_submodules", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": false - }, - "language": { - "name": "language", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "default_branch": { - "name": "default_branch", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "visibility": { - "name": "visibility", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "'public'" - }, - "status": { - "name": "status", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "'imported'" - }, - "last_mirrored": { - "name": "last_mirrored", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "error_message": { - "name": "error_message", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "destination_org": { - "name": "destination_org", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "created_at": { - "name": "created_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - }, - "updated_at": { - "name": "updated_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - } - }, - "indexes": { - "idx_repositories_user_id": { - "name": "idx_repositories_user_id", - "columns": [ - "user_id" - ], - "isUnique": false - }, - "idx_repositories_config_id": { - "name": "idx_repositories_config_id", - "columns": [ - "config_id" - ], - "isUnique": false - }, - "idx_repositories_status": { - "name": "idx_repositories_status", - "columns": [ - "status" - ], - "isUnique": false - }, - "idx_repositories_owner": { - "name": "idx_repositories_owner", - "columns": [ - "owner" - ], - "isUnique": false - }, - "idx_repositories_organization": { - "name": "idx_repositories_organization", - "columns": [ - "organization" - ], - "isUnique": false - }, - "idx_repositories_is_fork": { - "name": "idx_repositories_is_fork", - "columns": [ - "is_fork" - ], - "isUnique": false - }, - "idx_repositories_is_starred": { - "name": "idx_repositories_is_starred", - "columns": [ - "is_starred" - ], - "isUnique": false - } - }, - "foreignKeys": { - "repositories_user_id_users_id_fk": { - "name": "repositories_user_id_users_id_fk", - "tableFrom": "repositories", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "repositories_config_id_configs_id_fk": { - "name": "repositories_config_id_configs_id_fk", - "tableFrom": "repositories", - "tableTo": "configs", - "columnsFrom": [ - "config_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "sessions": { - "name": "sessions", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "expires_at": { - "name": "expires_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "ip_address": { - "name": "ip_address", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "user_agent": { - "name": "user_agent", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "created_at": { - "name": "created_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - }, - "updated_at": { - "name": "updated_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - } - }, - "indexes": { - "sessions_token_unique": { - "name": "sessions_token_unique", - "columns": [ - "token" - ], - "isUnique": true - }, - "idx_sessions_user_id": { - "name": "idx_sessions_user_id", - "columns": [ - "user_id" - ], - "isUnique": false - }, - "idx_sessions_token": { - "name": "idx_sessions_token", - "columns": [ - "token" - ], - "isUnique": false - }, - "idx_sessions_expires_at": { - "name": "idx_sessions_expires_at", - "columns": [ - "expires_at" - ], - "isUnique": false - } - }, - "foreignKeys": { - "sessions_user_id_users_id_fk": { - "name": "sessions_user_id_users_id_fk", - "tableFrom": "sessions", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "users": { - "name": "users", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "username": { - "name": "username", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "email": { - "name": "email", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "email_verified": { - "name": "email_verified", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": false - }, - "created_at": { - "name": "created_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - }, - "updated_at": { - "name": "updated_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "verification_tokens": { - "name": "verification_tokens", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "identifier": { - "name": "identifier", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "type": { - "name": "type", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "expires_at": { - "name": "expires_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "created_at": { - "name": "created_at", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(unixepoch())" - } - }, - "indexes": { - "verification_tokens_token_unique": { - "name": "verification_tokens_token_unique", - "columns": [ - "token" - ], - "isUnique": true - }, - "idx_verification_tokens_token": { - "name": "idx_verification_tokens_token", - "columns": [ - "token" - ], - "isUnique": false - }, - "idx_verification_tokens_identifier": { - "name": "idx_verification_tokens_identifier", - "columns": [ - "identifier" - ], - "isUnique": false - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - } - }, - "views": {}, - "enums": {}, - "_meta": { - "schemas": {}, - "tables": {}, - "columns": {} - }, - "internal": { - "indexes": {} - } -} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 3b8a751..1df77d8 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -5,15 +5,8 @@ { "idx": 0, "version": "6", - "when": 1752161775910, - "tag": "0000_big_xorn", - "breakpoints": true - }, - { - "idx": 1, - "version": "6", - "when": 1752166860985, - "tag": "0001_vengeful_whirlwind", + "when": 1752171873627, + "tag": "0000_init", "breakpoints": true } ] diff --git a/scripts/gitea-mirror-lxc-local.sh b/scripts/gitea-mirror-lxc-local.sh index 339b62a..2168c1b 100755 --- a/scripts/gitea-mirror-lxc-local.sh +++ b/scripts/gitea-mirror-lxc-local.sh @@ -7,7 +7,7 @@ CONTAINER="gitea-test" IMAGE="ubuntu:22.04" INSTALL_DIR="/opt/gitea-mirror" PORT=4321 -JWT_SECRET="$(openssl rand -hex 32)" +BETTER_AUTH_SECRET="$(openssl rand -hex 32)" BUN_ZIP="/tmp/bun-linux-x64.zip" BUN_URL="https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64.zip" @@ -73,7 +73,7 @@ Environment=NODE_ENV=production Environment=HOST=0.0.0.0 Environment=PORT=$PORT Environment=DATABASE_URL=file:data/gitea-mirror.db -Environment=JWT_SECRET=$JWT_SECRET +Environment=BETTER_AUTH_SECRET=$BETTER_AUTH_SECRET [Install] WantedBy=multi-user.target SERVICE diff --git a/src/components/auth/LoginForm.tsx b/src/components/auth/LoginForm.tsx index 963e551..421f5ab 100644 --- a/src/components/auth/LoginForm.tsx +++ b/src/components/auth/LoginForm.tsx @@ -33,7 +33,7 @@ export function LoginForm() { toast.success('Login successful!'); // Small delay before redirecting to see the success message setTimeout(() => { - window.location.href = '/dashboard'; + window.location.href = '/'; }, 1000); } catch (error) { showErrorToast(error, toast); diff --git a/src/components/auth/LoginPage.tsx b/src/components/auth/LoginPage.tsx new file mode 100644 index 0000000..abd8937 --- /dev/null +++ b/src/components/auth/LoginPage.tsx @@ -0,0 +1,10 @@ +import { LoginForm } from './LoginForm'; +import Providers from '@/components/layout/Providers'; + +export function LoginPage() { + return ( + + + + ); +} \ No newline at end of file diff --git a/src/components/auth/SignupForm.tsx b/src/components/auth/SignupForm.tsx index 6573733..77ee885 100644 --- a/src/components/auth/SignupForm.tsx +++ b/src/components/auth/SignupForm.tsx @@ -16,12 +16,11 @@ export function SignupForm() { setIsLoading(true); const form = e.currentTarget; const formData = new FormData(form); - const username = formData.get('username') as string | null; const email = formData.get('email') as string | null; const password = formData.get('password') as string | null; const confirmPassword = formData.get('confirmPassword') as string | null; - if (!username || !email || !password || !confirmPassword) { + if (!email || !password || !confirmPassword) { toast.error('Please fill in all fields'); setIsLoading(false); return; @@ -34,11 +33,13 @@ export function SignupForm() { } try { + // Derive username from email (part before @) + const username = email.split('@')[0]; await register(username, email, password); toast.success('Account created successfully! Redirecting to dashboard...'); // Small delay before redirecting to see the success message setTimeout(() => { - window.location.href = '/dashboard'; + window.location.href = '/'; }, 1500); } catch (error) { showErrorToast(error, toast); @@ -71,20 +72,6 @@ export function SignupForm() {
-
- - -
diff --git a/src/components/auth/SignupPage.tsx b/src/components/auth/SignupPage.tsx new file mode 100644 index 0000000..c864ea2 --- /dev/null +++ b/src/components/auth/SignupPage.tsx @@ -0,0 +1,10 @@ +import { SignupForm } from './SignupForm'; +import Providers from '@/components/layout/Providers'; + +export function SignupPage() { + return ( + + + + ); +} \ No newline at end of file diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts index 9a78cec..7bab564 100644 --- a/src/hooks/useAuth.ts +++ b/src/hooks/useAuth.ts @@ -37,28 +37,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const user = betterAuthSession.data?.user || null; const session = betterAuthSession.data || null; - // Check if this is first load and redirect if needed - useEffect(() => { - const checkFirstUser = async () => { - if (!betterAuthSession.isPending && !user) { - try { - // Check if there are any users in the system - const response = await fetch("/api/auth/check-users"); - if (response.status === 404) { - // No users found, redirect to signup - window.location.href = "/signup"; - } else if (!window.location.pathname.includes("/login")) { - // User not authenticated, redirect to login - window.location.href = "/login"; - } - } catch (err) { - console.error("Failed to check users:", err); - } - } - }; - - checkFirstUser(); - }, [betterAuthSession.isPending, user]); + // Don't do any redirects here - let the pages handle their own redirect logic const login = async (email: string, password: string) => { setIsLoading(true); @@ -67,7 +46,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const result = await authClient.signIn.email({ email, password, - callbackURL: "/dashboard", + callbackURL: "/", }); if (result.error) { @@ -93,9 +72,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const result = await authClient.signUp.email({ email, password, - name: username, // Better Auth uses 'name' field - username, // Also pass username as additional field - callbackURL: "/dashboard", + name: username, // Better Auth uses 'name' field for display name + callbackURL: "/", }); if (result.error) { diff --git a/src/lib/auth-config.ts b/src/lib/auth-config.ts index 1bea257..13ef64a 100644 --- a/src/lib/auth-config.ts +++ b/src/lib/auth-config.ts @@ -3,13 +3,6 @@ import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { sso, oidcProvider } from "better-auth/plugins"; import type { BunSQLiteDatabase } from "drizzle-orm/bun-sqlite"; -// Generate or use existing JWT secret -const JWT_SECRET = process.env.JWT_SECRET || process.env.BETTER_AUTH_SECRET; - -if (!JWT_SECRET) { - throw new Error("JWT_SECRET or BETTER_AUTH_SECRET environment variable is required"); -} - // This function will be called with the actual database instance export function createAuth(db: BunSQLiteDatabase) { return betterAuth({ diff --git a/src/lib/auth.ts b/src/lib/auth.ts index b0ef594..d120e5f 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -1,23 +1,22 @@ import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { db } from "./db"; - -// Generate or use existing JWT secret -const JWT_SECRET = process.env.JWT_SECRET || process.env.BETTER_AUTH_SECRET; - -if (!JWT_SECRET) { - throw new Error("JWT_SECRET or BETTER_AUTH_SECRET environment variable is required"); -} +import { db, users } from "./db"; +import * as schema from "./db/schema"; +import { eq } from "drizzle-orm"; export const auth = betterAuth({ // Database configuration database: drizzleAdapter(db, { provider: "sqlite", usePlural: true, // Our tables use plural names (users, not user) + schema, // Pass the schema explicitly }), + // Secret for signing tokens + secret: process.env.BETTER_AUTH_SECRET, + // Base URL configuration - baseURL: process.env.BETTER_AUTH_URL || "http://localhost:3000", + baseURL: process.env.BETTER_AUTH_URL || "http://localhost:4321", basePath: "/api/auth", // Specify the base path for auth endpoints // Authentication methods @@ -30,6 +29,7 @@ export const auth = betterAuth({ console.log("Reset URL:", url); }, }, + // Session configuration session: { @@ -44,9 +44,8 @@ export const auth = betterAuth({ // Keep the username field from our existing schema username: { type: "string", - required: true, - defaultValue: "user", // Default for migration - input: true, // Allow in signup form + required: false, + input: false, // Don't show in signup form - we'll derive from email } }, }, @@ -56,7 +55,7 @@ export const auth = betterAuth({ // Trusted origins for CORS trustedOrigins: [ - process.env.BETTER_AUTH_URL || "http://localhost:3000", + process.env.BETTER_AUTH_URL || "http://localhost:4321", ], }); diff --git a/src/lib/config.ts b/src/lib/config.ts index 8968830..3929b64 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -18,9 +18,9 @@ export const ENV = { return "sqlite://data/gitea-mirror.db"; }, - // JWT secret for authentication - JWT_SECRET: - process.env.JWT_SECRET || "your-secret-key-change-this-in-production", + // Better Auth secret for authentication + BETTER_AUTH_SECRET: + process.env.BETTER_AUTH_SECRET || "your-secret-key-change-this-in-production", // Server host and port HOST: process.env.HOST || "localhost", diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index cf84227..a400b2b 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -213,16 +213,18 @@ export const eventSchema = z.object({ export const users = sqliteTable("users", { id: text("id").primaryKey(), - username: text("username").notNull(), - password: text("password").notNull(), - email: text("email").notNull(), + name: text("name"), + email: text("email").notNull().unique(), emailVerified: integer("email_verified", { mode: "boolean" }).notNull().default(false), + image: text("image"), createdAt: integer("created_at", { mode: "timestamp" }) .notNull() .default(sql`(unixepoch())`), updatedAt: integer("updated_at", { mode: "timestamp" }) .notNull() .default(sql`(unixepoch())`), + // Custom fields + username: text("username"), }); export const events = sqliteTable("events", { @@ -463,9 +465,10 @@ export const sessions = sqliteTable("sessions", { // Accounts table (for OAuth providers and credentials) export const accounts = sqliteTable("accounts", { id: text("id").primaryKey(), + accountId: text("account_id").notNull(), userId: text("user_id").notNull().references(() => users.id), providerId: text("provider_id").notNull(), - providerUserId: text("provider_user_id").notNull(), + providerUserId: text("provider_user_id"), // Make nullable for email/password auth accessToken: text("access_token"), refreshToken: text("refresh_token"), expiresAt: integer("expires_at", { mode: "timestamp" }), @@ -478,6 +481,7 @@ export const accounts = sqliteTable("accounts", { .default(sql`(unixepoch())`), }, (table) => { return { + accountIdIdx: index("idx_accounts_account_id").on(table.accountId), userIdIdx: index("idx_accounts_user_id").on(table.userId), providerIdx: index("idx_accounts_provider").on(table.providerId, table.providerUserId), }; diff --git a/src/pages/api/auth/debug.ts b/src/pages/api/auth/debug.ts new file mode 100644 index 0000000..5dfdbc6 --- /dev/null +++ b/src/pages/api/auth/debug.ts @@ -0,0 +1,72 @@ +import type { APIRoute } from "astro"; +import { auth } from "@/lib/auth"; +import { db } from "@/lib/db"; +import { users } from "@/lib/db/schema"; +import { nanoid } from "nanoid"; + +export const GET: APIRoute = async ({ request }) => { + try { + // Get Better Auth configuration info + const info = { + baseURL: auth.options.baseURL, + basePath: auth.options.basePath, + trustedOrigins: auth.options.trustedOrigins, + emailPasswordEnabled: auth.options.emailAndPassword?.enabled, + userFields: auth.options.user?.additionalFields, + databaseConfig: { + usePlural: true, + provider: "sqlite" + } + }; + + return new Response(JSON.stringify({ + success: true, + config: info + }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } catch (error) { + return new Response(JSON.stringify({ + success: false, + error: error instanceof Error ? error.message : "Unknown error", + }), { + status: 500, + headers: { "Content-Type": "application/json" }, + }); + } +}; + +export const POST: APIRoute = async ({ request }) => { + try { + // Test creating a user directly + const userId = nanoid(); + const now = new Date(); + + await db.insert(users).values({ + id: userId, + email: "test2@example.com", + emailVerified: false, + username: "test2", + // Let the database handle timestamps with defaults + }); + + return new Response(JSON.stringify({ + success: true, + userId, + message: "User created successfully" + }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } catch (error) { + return new Response(JSON.stringify({ + success: false, + error: error instanceof Error ? error.message : "Unknown error", + details: error + }), { + status: 500, + headers: { "Content-Type": "application/json" }, + }); + } +}; \ No newline at end of file diff --git a/src/pages/login.astro b/src/pages/login.astro index 9ac2759..e27a6db 100644 --- a/src/pages/login.astro +++ b/src/pages/login.astro @@ -1,7 +1,7 @@ --- import '../styles/global.css'; import ThemeScript from '@/components/theme/ThemeScript.astro'; -import { LoginForm } from '@/components/auth/LoginForm'; +import { LoginPage } from '@/components/auth/LoginPage'; import { db, users } from '@/lib/db'; import { sql } from 'drizzle-orm'; @@ -30,7 +30,7 @@ const generator = Astro.generator;
- +
diff --git a/src/pages/signup.astro b/src/pages/signup.astro index 71a96ee..3dcfc4c 100644 --- a/src/pages/signup.astro +++ b/src/pages/signup.astro @@ -1,7 +1,7 @@ --- import '../styles/global.css'; import ThemeScript from '@/components/theme/ThemeScript.astro'; -import { SignupForm } from '@/components/auth/SignupForm'; +import { SignupPage } from '@/components/auth/SignupPage'; import { db, users } from '@/lib/db'; import { sql } from 'drizzle-orm'; @@ -34,7 +34,7 @@ const generator = Astro.generator;

Welcome to Gitea Mirror

Let's set up your administrator account to get started.

- +