mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 16:38:19 +01:00
feat(api): ✨ Add new endpoint to get a user by its username
This commit is contained in:
parent
407eb5e205
commit
be881f18cd
|
|
@ -16,6 +16,7 @@ Lysand Server `0.7.0` is backwards compatible with `0.6.0`. However, some new fe
|
||||||
- Added [**TOS and Privacy Policy**](docs/api/mastodon.md) endpoints.
|
- Added [**TOS and Privacy Policy**](docs/api/mastodon.md) endpoints.
|
||||||
- Added [**Challenge API**](docs/api/challenges.md). (basically CAPTCHAS). This can be enabled/disabled by administrators. No `lysand-fe` support yet.
|
- Added [**Challenge API**](docs/api/challenges.md). (basically CAPTCHAS). This can be enabled/disabled by administrators. No `lysand-fe` support yet.
|
||||||
- Added ability to change the `username` of a user. ([Mastodon API extension](docs/api/mastodon.md)).
|
- Added ability to change the `username` of a user. ([Mastodon API extension](docs/api/mastodon.md)).
|
||||||
|
- Added an endpoint to get a user by its username.
|
||||||
- Add OpenID Connect registration support. Admins can now disable username/password registration entirely and still allow users to sign up via OpenID Connect.
|
- Add OpenID Connect registration support. Admins can now disable username/password registration entirely and still allow users to sign up via OpenID Connect.
|
||||||
- Add option to never convert vector images to a raster format.
|
- Add option to never convert vector images to a raster format.
|
||||||
- Refactor logging system to be more robust and easier to use. Logfiles are now automatically rotated.
|
- Refactor logging system to be more robust and easier to use. Logfiles are now automatically rotated.
|
||||||
|
|
|
||||||
|
|
@ -219,3 +219,21 @@ This request is authenticated with the user's Mastodon API access token.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Get User By Username
|
||||||
|
|
||||||
|
Gets a user by their username.
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/v1/users/id?username=myCoolUser
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response
|
||||||
|
|
||||||
|
Returns an account object.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// 200 OK
|
||||||
|
{
|
||||||
|
id: string;
|
||||||
|
// Account object
|
||||||
|
}
|
||||||
|
|
@ -50,12 +50,13 @@ Contains the same extensions as `/api/v1/instance`, except `banner` which uses t
|
||||||
|
|
||||||
(`/api/v1/accounts/:id`, `/api/v1/accounts/verify_credentials`, ...)
|
(`/api/v1/accounts/:id`, `/api/v1/accounts/verify_credentials`, ...)
|
||||||
|
|
||||||
An extra attribute has been adding to all returned account objects:
|
Two extra attributes has been adding to all returned account objects:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
roles: LysandRoles[];
|
roles: LysandRoles[];
|
||||||
|
uri: string;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -63,7 +64,11 @@ An extra attribute has been adding to all returned account objects:
|
||||||
|
|
||||||
An array of roles from [Lysand Roles](./roles.md).
|
An array of roles from [Lysand Roles](./roles.md).
|
||||||
|
|
||||||
### `/api/v1/accounts/update_credentials`
|
### `uri`
|
||||||
|
|
||||||
|
The URI of the account's Lysand object (for federation). Similar to Mastodon's `uri` field on notes.
|
||||||
|
|
||||||
|
## `/api/v1/accounts/update_credentials`
|
||||||
|
|
||||||
The `username` parameter can now (optionally) be set to change the user's handle.
|
The `username` parameter can now (optionally) be set to change the user's handle.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@
|
||||||
"@inquirer/input": "^2.2.1",
|
"@inquirer/input": "^2.2.1",
|
||||||
"@json2csv/plainjs": "^7.0.6",
|
"@json2csv/plainjs": "^7.0.6",
|
||||||
"@logtape/logtape": "npm:@jsr/logtape__logtape@0.4.2",
|
"@logtape/logtape": "npm:@jsr/logtape__logtape@0.4.2",
|
||||||
"@lysand-org/client": "^0.2.3",
|
"@lysand-org/client": "^0.2.4",
|
||||||
"@lysand-org/federation": "^2.1.1",
|
"@lysand-org/federation": "^2.1.1",
|
||||||
"@oclif/core": "^4.0.12",
|
"@oclif/core": "^4.0.12",
|
||||||
"@tufjs/canonical-json": "^2.0.0",
|
"@tufjs/canonical-json": "^2.0.0",
|
||||||
|
|
|
||||||
|
|
@ -636,6 +636,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
||||||
username: user.username,
|
username: user.username,
|
||||||
display_name: user.displayName,
|
display_name: user.displayName,
|
||||||
note: user.note,
|
note: user.note,
|
||||||
|
uri: this.getUri(),
|
||||||
url:
|
url:
|
||||||
user.uri ||
|
user.uri ||
|
||||||
new URL(`/@${user.username}`, config.http.base_url).toString(),
|
new URL(`/@${user.username}`, config.http.base_url).toString(),
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ describe(meta.route, () => {
|
||||||
statuses_count: 40,
|
statuses_count: 40,
|
||||||
note: users[0].data.note,
|
note: users[0].data.note,
|
||||||
acct: users[0].data.username,
|
acct: users[0].data.username,
|
||||||
|
uri: expect.any(String),
|
||||||
url: expect.any(String),
|
url: expect.any(String),
|
||||||
avatar_static: expect.any(String),
|
avatar_static: expect.any(String),
|
||||||
header_static: expect.any(String),
|
header_static: expect.any(String),
|
||||||
|
|
|
||||||
44
server/api/api/v1/accounts/id/index.test.ts
Normal file
44
server/api/api/v1/accounts/id/index.test.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { afterAll, describe, expect, test } from "bun:test";
|
||||||
|
import type { Account as ApiAccount } from "@lysand-org/client/types";
|
||||||
|
import { config } from "config-manager";
|
||||||
|
import { getTestUsers, sendTestRequest } from "~/tests/utils";
|
||||||
|
import { meta } from "./index";
|
||||||
|
|
||||||
|
const { users, deleteUsers } = await getTestUsers(5);
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await deleteUsers();
|
||||||
|
});
|
||||||
|
|
||||||
|
// /api/v1/accounts/id
|
||||||
|
describe(meta.route, () => {
|
||||||
|
test("should correctly get user from username", async () => {
|
||||||
|
const response = await sendTestRequest(
|
||||||
|
new Request(
|
||||||
|
new URL(
|
||||||
|
`${meta.route}?username=${users[0].data.username}`,
|
||||||
|
config.http.base_url,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
|
||||||
|
const data = (await response.json()) as ApiAccount;
|
||||||
|
|
||||||
|
expect(data.id).toBe(users[0].id);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return 404 for non-existent user", async () => {
|
||||||
|
const response = await sendTestRequest(
|
||||||
|
new Request(
|
||||||
|
new URL(
|
||||||
|
`${meta.route}?username=${users[0].data.username}-nonexistent`,
|
||||||
|
config.http.base_url,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.status).toBe(404);
|
||||||
|
});
|
||||||
|
});
|
||||||
51
server/api/api/v1/accounts/id/index.ts
Normal file
51
server/api/api/v1/accounts/id/index.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { applyConfig, auth, handleZodError } from "@/api";
|
||||||
|
import { errorResponse, jsonResponse } from "@/response";
|
||||||
|
import type { Hono } from "@hono/hono";
|
||||||
|
import { zValidator } from "@hono/zod-validator";
|
||||||
|
import { and, eq, isNull } from "drizzle-orm";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { RolePermissions, Users } from "~/drizzle/schema";
|
||||||
|
import { User } from "~/packages/database-interface/user";
|
||||||
|
|
||||||
|
export const meta = applyConfig({
|
||||||
|
allowedMethods: ["GET"],
|
||||||
|
ratelimits: {
|
||||||
|
max: 30,
|
||||||
|
duration: 60,
|
||||||
|
},
|
||||||
|
route: "/api/v1/accounts/id",
|
||||||
|
auth: {
|
||||||
|
required: false,
|
||||||
|
oauthPermissions: [],
|
||||||
|
},
|
||||||
|
permissions: {
|
||||||
|
required: [RolePermissions.Search],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemas = {
|
||||||
|
query: z.object({
|
||||||
|
username: z.string().min(1).max(512),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (app: Hono) =>
|
||||||
|
app.on(
|
||||||
|
meta.allowedMethods,
|
||||||
|
meta.route,
|
||||||
|
zValidator("query", schemas.query, handleZodError),
|
||||||
|
auth(meta.auth, meta.permissions),
|
||||||
|
async (context) => {
|
||||||
|
const { username } = context.req.valid("query");
|
||||||
|
|
||||||
|
const user = await User.fromSql(
|
||||||
|
and(eq(Users.username, username), isNull(Users.instanceId)),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return errorResponse("User not found", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResponse(user.toApi());
|
||||||
|
},
|
||||||
|
);
|
||||||
Loading…
Reference in a new issue