diff --git a/api/src/endpoints/add_repo.rs b/api/src/endpoints/add_repo.rs index 971db42..3b6b8d2 100644 --- a/api/src/endpoints/add_repo.rs +++ b/api/src/endpoints/add_repo.rs @@ -1,6 +1,5 @@ use crate::{auth::User, error::Result, user::RepositorySchema, validate::validate_repo}; use actix_web::{HttpResponse, web}; -use serde::Serialize; use crate::AppState; diff --git a/api/src/endpoints/global_repos.rs b/api/src/endpoints/global_repos.rs index dbbb9da..4d3fb8a 100644 --- a/api/src/endpoints/global_repos.rs +++ b/api/src/endpoints/global_repos.rs @@ -4,5 +4,6 @@ use actix_web::{HttpResponse, web}; use crate::AppState; pub async fn global_repos(app_state: web::Data) -> Result { - Ok(HttpResponse::Ok().finish()) + let repos = app_state.user.global_repositories().await?; + Ok(repos) } diff --git a/api/src/user.rs b/api/src/user.rs index 3b25eb4..d01ade2 100644 --- a/api/src/user.rs +++ b/api/src/user.rs @@ -76,8 +76,15 @@ impl UserRepository { .await? .items() .iter() - .filter_map(|item| Some(item.get("full_name")?.as_s().ok()?.to_string())) + .filter_map(|item| { + if (*item.get("approved")?.as_bool().ok()?) == false { + return None; + }; + Some(item.get("full_name")?.as_s().ok()?.to_string()) + }) .collect::>(); + + Ok(HttpResponse::Ok().json(response)) } pub async fn add_repository( @@ -97,6 +104,7 @@ impl UserRepository { .item("gsi1pk", AttributeValue::S("REPOS".into())) .item("gsi1sk", AttributeValue::S(now.clone())) .item("imported_at", AttributeValue::S(now)) + .item("approved", AttributeValue::Bool(false)) .send() .await; diff --git a/drizzle/0002_sparkling_siren.sql b/drizzle/0002_sparkling_siren.sql new file mode 100644 index 0000000..d8b78c0 --- /dev/null +++ b/drizzle/0002_sparkling_siren.sql @@ -0,0 +1,5 @@ +ALTER TABLE "session" ADD COLUMN "impersonated_by" text;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "role" text;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "banned" boolean DEFAULT false;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "ban_reason" text;--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "ban_expires" timestamp; \ No newline at end of file diff --git a/drizzle/meta/0002_snapshot.json b/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..9f34ac7 --- /dev/null +++ b/drizzle/meta/0002_snapshot.json @@ -0,0 +1,448 @@ +{ + "id": "701e3de7-291e-439d-9cd2-a3457ab8ac60", + "prevId": "2d1e972c-8275-4278-a835-e8bc5d477168", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "account_userId_idx": { + "name": "account_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.jwks": { + "name": "jwks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "session_userId_idx": { + "name": "session_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 218d2b9..894b0bb 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -15,6 +15,13 @@ "when": 1768455615596, "tag": "0001_abnormal_swordsman", "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1768954428861, + "tag": "0002_sparkling_siren", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/lib/auth-client.ts b/src/lib/auth-client.ts index db6f1d4..4ad6455 100644 --- a/src/lib/auth-client.ts +++ b/src/lib/auth-client.ts @@ -1,7 +1,7 @@ -import { jwtClient } from 'better-auth/client/plugins'; +import { jwtClient, adminClient } from 'better-auth/client/plugins'; import { createAuthClient } from 'better-auth/svelte'; export const authClient = createAuthClient({ - baseURL: 'http://localhost:5173', - plugins: [jwtClient()] + baseURL: 'http://localhost:5173', + plugins: [jwtClient(), adminClient()] }); diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 07a0c40..6ea8524 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -2,14 +2,14 @@ import { betterAuth } from 'better-auth'; import { drizzleAdapter } from 'better-auth/adapters/drizzle'; import { db } from './db/drizzle'; import * as authSchema from './db/auth-schema'; -import { jwt } from 'better-auth/plugins'; +import { jwt, admin } from 'better-auth/plugins'; export const auth = betterAuth({ database: drizzleAdapter(db, { provider: 'pg', schema: { ...authSchema } }), - plugins: [jwt()], + plugins: [jwt(), admin()], socialProviders: { github: { clientId: process.env.GH_CLIENT_ID!, diff --git a/src/lib/db/auth-schema.ts b/src/lib/db/auth-schema.ts index b26d5a8..4160768 100644 --- a/src/lib/db/auth-schema.ts +++ b/src/lib/db/auth-schema.ts @@ -12,6 +12,10 @@ export const user = pgTable("user", { .defaultNow() .$onUpdate(() => /* @__PURE__ */ new Date()) .notNull(), + role: text("role"), + banned: boolean("banned").default(false), + banReason: text("ban_reason"), + banExpires: timestamp("ban_expires"), }); export const session = pgTable( @@ -29,6 +33,7 @@ export const session = pgTable( userId: text("user_id") .notNull() .references(() => user.id, { onDelete: "cascade" }), + impersonatedBy: text("impersonated_by"), }, (table) => [index("session_userId_idx").on(table.userId)], );