-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #17 from planetary-social/add-metadata
Adds agent, ip and updated_at to entries
- Loading branch information
Showing
7 changed files
with
199 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { AppError } from "./errors.js"; | ||
export default class NameRecord { | ||
constructor( | ||
name, | ||
pubkey, | ||
relays = [], | ||
clientIp = "", | ||
userAgent = "", | ||
updated_at | ||
) { | ||
validateName(name); | ||
|
||
this.name = name; | ||
this.pubkey = pubkey; | ||
this.relays = relays; | ||
this.clientIp = clientIp; | ||
this.userAgent = userAgent; | ||
this.updated_at = updated_at; | ||
} | ||
} | ||
|
||
export function validateName(name) { | ||
if (name.length < 3) { | ||
throw new AppError( | ||
422, | ||
`Name '${name}' should have more than 3 characters.` | ||
); | ||
} | ||
|
||
if (name.startsWith("-")) { | ||
throw new AppError(422, `Name '${name}' should not start with a hyphen -.`); | ||
} | ||
|
||
if (name.endsWith("-")) { | ||
throw new AppError(422, `Name '${name}' should not start with a hyphen -.`); | ||
} | ||
|
||
if (name.includes("_")) { | ||
throw new AppError( | ||
422, | ||
`Name '${name}' should not include an underscore _.` | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import NameRecord from "./nameRecord.js"; | ||
import { AppError } from "./errors.js"; | ||
|
||
const MAX_ENTRIES = 1000; | ||
export default class NameRecordRepository { | ||
constructor(redisClient) { | ||
this.redis = redisClient; | ||
} | ||
|
||
async findByName(name) { | ||
const luaScript = ` | ||
local pubkey = redis.call('GET', 'pubkey:' .. KEYS[1]) | ||
if not pubkey then return nil end | ||
local relays = redis.call('SMEMBERS', 'relays:' .. pubkey) | ||
local userAgent = redis.call('GET', 'user_agent:' .. pubkey) | ||
local clientIp = redis.call('GET', 'ip:' .. pubkey) | ||
local updatedAt = redis.call('GET', 'updated_at:' .. pubkey) | ||
return {pubkey, relays, userAgent, clientIp, updatedAt} | ||
`; | ||
|
||
const result = await this.redis.eval(luaScript, 1, name); | ||
if (!result) return null; | ||
|
||
const [pubkey, relays, userAgent, clientIp, updatedAt] = result; | ||
|
||
return new NameRecord(name, pubkey, relays, clientIp, userAgent, updatedAt); | ||
} | ||
|
||
async save(nameRecord) { | ||
const { name, pubkey, relays, clientIp, userAgent } = nameRecord; | ||
const updated_at = new Date().toISOString(); | ||
const timestamp = new Date(updated_at).getTime() / 1000; // Convert to UNIX timestamp | ||
|
||
const currentPubkey = await this.redis.get(`pubkey:${name}`); | ||
if (currentPubkey && currentPubkey !== pubkey) { | ||
throw new AppError( | ||
409, | ||
"Conflict: pubkey already exists, you can only change associated relays." | ||
); | ||
} | ||
|
||
const pipeline = this.redis.multi(); | ||
pipeline.set(`pubkey:${name}`, pubkey); | ||
|
||
pipeline.del(`relays:${pubkey}`); | ||
if (relays && relays.length) { | ||
pipeline.sadd(`relays:${pubkey}`, ...relays); | ||
} | ||
if (clientIp) { | ||
pipeline.set(`ip:${pubkey}`, clientIp); | ||
} | ||
if (userAgent) { | ||
pipeline.set(`user_agent:${pubkey}`, userAgent); | ||
} | ||
pipeline.set(`updated_at:${pubkey}`, updated_at); | ||
|
||
pipeline.zadd(`name_record_updates`, timestamp, name); | ||
// Keep the latest maxEntries records by removing older ones | ||
pipeline.zremrangebyrank(`name_record_updates`, 0, -(MAX_ENTRIES + 1)); | ||
|
||
await pipeline.exec(); | ||
} | ||
|
||
async deleteByName(name) { | ||
const pubkey = await this.redis.get(`pubkey:${name}`); | ||
if (!pubkey) return false; | ||
|
||
const pipeline = this.redis.multi(); | ||
pipeline.del(`pubkey:${name}`); | ||
pipeline.del(`relays:${pubkey}`); | ||
pipeline.del(`ip:${pubkey}`); | ||
pipeline.del(`user_agent:${pubkey}`); | ||
pipeline.del(`updated_at:${pubkey}`); | ||
pipeline.zrem(`name_record_updates`, name); | ||
|
||
await pipeline.exec(); | ||
return true; | ||
} | ||
|
||
async findLatest(limit = 10) { | ||
const names = await this.redis.zrevrange("nameRecordUpdates", 0, limit - 1); | ||
const records = await Promise.all( | ||
names.map((name) => this.findByName(name)) | ||
); | ||
|
||
return records; // These are sorted by updated_at due to the sorted set's ordering | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters