diff --git a/api/src/api/users.rs b/api/src/api/users.rs
index 975cfb4a..3b394168 100644
--- a/api/src/api/users.rs
+++ b/api/src/api/users.rs
@@ -13,6 +13,7 @@ use crate::util::ApiResult;
use crate::util::RequestIdExt;
use super::ApiError;
+use super::ApiPackage;
use super::ApiScope;
use super::ApiUser;
@@ -20,6 +21,7 @@ pub fn users_router() -> Router
{
Router::builder()
.get("/:id", util::json(get_handler))
.get("/:id/scopes", util::json(get_scopes_handler))
+ .get("/:id/packages", util::json(get_packages_handler))
.build()
.unwrap()
}
@@ -54,3 +56,25 @@ pub async fn get_scopes_handler(
Ok(scopes.into_iter().map(ApiScope::from).collect())
}
+
+#[instrument(name = "GET /api/users/:id/packages", skip(req), err, fields(id))]
+pub async fn get_packages_handler(
+ req: Request,
+) -> ApiResult> {
+ let id = req.param_uuid("id")?;
+ Span::current().record("id", field::display(id));
+
+ let db = req.data::().unwrap();
+ db.get_user_public(id)
+ .await?
+ .ok_or(ApiError::UserNotFound)?;
+
+ let packages = db.get_recent_packages_by_user(&id).await?;
+
+ Ok(
+ packages
+ .into_iter()
+ .map(|package| ApiPackage::from((package, None, Default::default())))
+ .collect(),
+ )
+}
diff --git a/api/src/db/database.rs b/api/src/db/database.rs
index 06af6a26..30ff4433 100644
--- a/api/src/db/database.rs
+++ b/api/src/db/database.rs
@@ -3215,6 +3215,39 @@ impl Database {
.fetch_all(&self.pool)
.await
}
+
+ pub async fn get_recent_packages_by_user(
+ &self,
+ user_id: &uuid::Uuid,
+ ) -> Result> {
+ let packages = sqlx::query_as!(
+ Package,
+ r#"
+ SELECT DISTINCT ON (packages.scope, packages.name)
+ packages.scope as "scope: ScopeName",
+ packages.name as "name: PackageName",
+ packages.description,
+ packages.github_repository_id,
+ packages.runtime_compat as "runtime_compat: RuntimeCompat",
+ packages.when_featured,
+ packages.is_archived,
+ packages.updated_at,
+ packages.created_at,
+ (SELECT COUNT(created_at) FROM package_versions WHERE scope = packages.scope AND name = packages.name) as "version_count!",
+ (SELECT version FROM package_versions WHERE scope = packages.scope AND name = packages.name AND version NOT LIKE '%-%' AND is_yanked = false ORDER BY version DESC LIMIT 1) as "latest_version"
+ FROM packages
+ JOIN scope_members ON packages.scope = scope_members.scope
+ WHERE scope_members.user_id = $1
+ ORDER BY packages.scope, packages.name, packages.created_at DESC
+ LIMIT 10;
+ "#,
+ user_id
+ )
+ .fetch_all(&self.pool)
+ .await?;
+
+ Ok(packages)
+ }
}
async fn finalize_package_creation(
diff --git a/frontend/routes/user/[id].tsx b/frontend/routes/user/[id].tsx
index f1c22c7d..45cc840f 100644
--- a/frontend/routes/user/[id].tsx
+++ b/frontend/routes/user/[id].tsx
@@ -2,7 +2,7 @@
import { HttpError } from "fresh";
import { define } from "../../util.ts";
import { path } from "../../utils/api.ts";
-import { FullUser, Scope, User } from "../../utils/api_types.ts";
+import { FullUser, Package, Scope, User } from "../../utils/api_types.ts";
import { ListPanel } from "../../components/ListPanel.tsx";
import { AccountLayout } from "../account/(_components)/AccountLayout.tsx";
@@ -32,14 +32,26 @@ export default define.page(function UserPage({ data, state }) {
)}
- {
- /*
-
Recently published
-
- TODO: all packages recently published by this user
-
-
*/
- }
+ {data.packages.length > 0
+ ? (
+ ({
+ value: `@${pkg.scope}/${pkg.name}`,
+ //@am/neuralnetwork
+ href: `/${pkg.scope}/${pkg.name}`,
+ }))}
+ />
+ )
+ : (
+
+ {state.user?.id === data.user.id ? "You have" : "This user has"}
+ {" "}
+ not published any packages recently.
+
+ )}
);
@@ -47,10 +59,11 @@ export default define.page(function UserPage({ data, state }) {
export const handler = define.handlers({
async GET(ctx) {
- const [currentUser, userRes, scopesRes] = await Promise.all([
+ const [currentUser, userRes, scopesRes, packagesRes] = await Promise.all([
ctx.state.userPromise,
ctx.state.api.get(path`/users/${ctx.params.id}`),
ctx.state.api.get(path`/users/${ctx.params.id}/scopes`),
+ ctx.state.api.get(path`/users/${ctx.params.id}/packages`),
]);
if (currentUser instanceof Response) return currentUser;
@@ -62,6 +75,7 @@ export const handler = define.handlers({
throw userRes; // gracefully handle errors
}
if (!scopesRes.ok) throw scopesRes; // gracefully handle errors
+ if (!packagesRes.ok) throw packagesRes; // gracefully handle errors
let user: User | FullUser = userRes.data;
if (ctx.params.id === currentUser?.id) {
@@ -75,6 +89,7 @@ export const handler = define.handlers({
data: {
user,
scopes: scopesRes.data,
+ packages: packagesRes.data,
},
};
},