-
Notifications
You must be signed in to change notification settings - Fork 0
/
lockstate.ts
145 lines (117 loc) · 2.8 KB
/
lockstate.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { ulid, v1 } from "./deps.ts";
export class UnauthorizedError extends Error {
name = "UnauthorizedError";
}
export class NotFoundError extends Error {
name = "NotFound";
}
export interface LockStateDTO {
i: string;
k: string;
v: boolean;
e: number;
}
export class LockState {
static create() {
return new LockState({
e: -1,
i: ulid().toString().toLowerCase(),
k: v1.generate().toString().toLowerCase(),
v: false,
});
}
public readonly i!: string;
public readonly k!: string;
public v!: boolean;
public e!: number;
constructor(obj: LockStateDTO) {
Object.assign(this, obj);
}
toggle() {
this.v = !this.v;
}
lock(expireTime: number) {
this.v = true;
if (expireTime > -1) {
this.e = Date.now() + (expireTime * 1000);
}
}
unlock() {
this.v = false;
this.e = -1;
}
isLocked(now = Date.now()) {
if (this.v === false) {
return false;
}
return this.checkIsExpired(now) === false;
}
isValidKey(k: string) {
if (this.k !== k) {
throw new UnauthorizedError("key is not valid");
}
return true;
}
getId() {
return this.i;
}
getKey() {
return this.k;
}
private checkIsExpired(now: number): boolean {
return this.isExpiring() ? now > this.e : false;
}
private isExpiring() {
return this.e > -1;
}
}
export class LockStateAppService {
constructor(private readonly kv: Deno.Kv) {
}
private async save(lock: LockState) {
await this.kv.set(["locks", lock.getId()], lock);
}
private async get(lockId: string): Promise<LockState> {
const lock = await this.kv.get<LockStateDTO>([
"locks",
lockId.toLowerCase(),
]);
if (!lock.value) {
throw new NotFoundError("Lock not found");
}
return new LockState(lock.value);
}
async lockByIdAndKey(lockId: string, lockKey: string, expireTime: number) {
const lock = await this.get(lockId);
lock.isValidKey(lockKey);
lock.lock(expireTime);
await this.save(lock);
return lock.isLocked();
}
async unlock(lockId: string, lockKey: string) {
const lock = await this.get(lockId);
lock.isValidKey(lockKey);
lock.unlock();
await this.save(lock);
return lock.isLocked();
}
async toggle(lockId: string, lockKey: string): Promise<boolean> {
const lock = await this.get(lockId);
lock.isValidKey(lockKey);
lock.toggle();
await this.save(lock);
return lock.isLocked();
}
async isLocked(lockId: string): Promise<boolean> {
const lock = await this.get(lockId);
return lock.isLocked();
}
async createNew(locked: boolean, expireTime: number = -1) {
const newLock = LockState.create();
if (locked) {
newLock.lock(expireTime)
}
await this.save(newLock);
return newLock;
}
}