Skip to content
GitLab
Explore
Sign in
Register
Hide whitespace changes
Inline
Side-by-side
packages/lib/src/net.ts
View file @
51eacdaf
// socket.io
export
type
Subscription
=
"
heatmap
"
;
export
interface
ServerToClientEvents
{
canvas
:
(
pixels
:
string
[])
=>
void
;
canvas
:
(
start
:
[
x
:
number
,
y
:
number
],
end
:
[
x
:
number
,
y
:
number
],
pixels
:
string
[]
)
=>
void
;
clearCanvasChunks
:
()
=>
void
;
user
:
(
user
:
AuthSession
)
=>
void
;
standing
:
(
standing
:
IAccountStanding
)
=>
void
;
config
:
(
config
:
ClientConfig
)
=>
void
;
pixel
:
(
pixel
:
Pixel
)
=>
void
;
online
:
(
count
:
{
count
:
number
})
=>
void
;
availablePixels
:
(
count
:
number
)
=>
void
;
pixelLastPlaced
:
(
time
:
number
)
=>
void
;
undo
:
(
data
:
{
available
:
false
}
|
{
available
:
true
;
expireAt
:
number
}
)
=>
void
;
square
:
(
start
:
[
x
:
number
,
y
:
number
],
end
:
[
x
:
number
,
y
:
number
],
color
:
number
)
=>
void
;
alert
:
(
alert
:
IAlert
)
=>
void
;
alert_dismiss
:
(
id
:
string
)
=>
void
;
recaptcha
:
(
site_key
:
string
)
=>
void
;
recaptcha_challenge
:
(
ack
:
(
token
:
string
)
=>
void
)
=>
void
;
/* --- subscribe events --- */
/**
* Emitted to room `sub:heatmap`
* @param heatmap
* @returns
*/
heatmap
:
(
heatmap
:
string
)
=>
void
;
}
export
interface
ClientToServerEvents
{
place
:
(
pixel
:
Pixel
,
bypassCooldown
:
boolean
,
ack
:
(
_
:
PacketAck
<
Pixel
,
"
no_user
"
|
"
invalid_pixel
"
|
"
pixel_cooldown
"
|
"
palette_color_invalid
"
|
"
canvas_frozen
"
|
"
no_user
"
|
"
invalid_pixel
"
|
"
pixel_cooldown
"
|
"
palette_color_invalid
"
|
"
you_already_placed_that
"
|
"
banned
"
|
"
pixel_already_pending
"
>
)
=>
void
)
=>
void
;
undo
:
(
ack
:
(
_
:
PacketAck
<
{},
"
canvas_frozen
"
|
"
no_user
"
|
"
unavailable
"
|
"
pixel_covered
"
>
)
=>
void
)
=>
void
;
}
// app context
export
interface
IAppContext
{
config
:
ClientConfig
;
user
?:
AuthSession
;
canvasPosition
?:
ICanvasPosition
;
setCanvasPosition
:
(
v
:
ICanvasPosition
)
=>
void
;
cursorPosition
?:
IPosition
;
setCursorPosition
:
(
v
?:
IPosition
)
=>
void
;
pixels
:
{
available
:
number
};
}
export
interface
IPalleteContext
{
color
?:
number
;
subscribe
:
(
topic
:
Subscription
)
=>
void
;
unsubscribe
:
(
topic
:
Subscription
)
=>
void
;
}
export
interface
I
Canvas
Position
{
export
interface
IPosition
{
x
:
number
;
y
:
number
;
zoom
:
number
;
}
export
interface
IPosition
{
x
:
number
;
y
:
number
;
export
type
IAccountStanding
=
|
{
banned
:
false
;
}
|
{
banned
:
true
;
/**
* ISO timestamp
*/
until
:
string
;
reason
?:
string
;
};
/**
* Typescript magic
*
* key => name of the event
* value => what metadata the message will include
*/
export
interface
IAlertKeyedMessages
{
banned
:
{
/**
* ISO date
*/
until
:
string
;
};
unbanned
:
{};
}
export
type
IAlert
<
Is
extends
"
toast
"
|
"
modal
"
=
"
toast
"
|
"
modal
"
>
=
{
is
:
Is
;
action
:
"
system
"
|
"
moderation
"
;
id
?:
string
;
}
&
(
|
{
is
:
"
toast
"
;
severity
:
"
info
"
|
"
success
"
|
"
warning
"
|
"
error
"
|
"
default
"
;
autoDismiss
:
boolean
;
}
|
{
is
:
"
modal
"
;
dismissable
:
boolean
;
}
)
&
(
IAlertKeyed
|
{
title
:
string
;
body
?:
string
});
/**
* Typescript magic
*
* #metadata depends on message_key and is mapped via IAlertKeyedMessages
*/
type
IAlertKeyed
=
keyof
IAlertKeyedMessages
extends
infer
MessageKey
?
MessageKey
extends
keyof
IAlertKeyedMessages
?
{
message_key
:
MessageKey
;
metadata
:
IAlertKeyedMessages
[
MessageKey
];
}
:
never
:
never
;
// other
export
type
Pixel
=
{
x
:
number
;
y
:
number
;
/**
* Palette color ID or -1 for nothing
*/
color
:
number
;
};
...
...
@@ -65,20 +156,51 @@ export type PalleteColor = {
export
type
CanvasConfig
=
{
size
:
[
number
,
number
];
frozen
:
boolean
;
zoom
:
number
;
pixel
:
{
maxStack
:
number
;
cooldown
:
number
;
multiplier
:
number
;
};
undo
:
{
/**
* time in ms to allow undos
*/
grace_period
:
number
;
};
};
export
type
ClientConfig
=
{
/**
* Monolith git hash, if it doesn't match, client will reload
*/
version
:
string
;
pallete
:
{
colors
:
PalleteColor
[];
/**
* ms for cooldown
* @see CanvasLib#getPixelCooldown
*/
pixel_cooldown
:
number
;
};
canvas
:
CanvasConfig
;
chat
:
{
enabled
:
boolean
;
/**
* @example aftermath.gg
*/
matrix_homeserver
:
string
;
/**
* @example https://chat.fediverse.events
*/
element_host
:
string
;
/**
* URI encoded alias
* @example %23canvas-general:aftermath.gg
*/
general_alias
:
string
;
};
};
/**
...
...
@@ -97,13 +219,20 @@ export type AuthSession = {
software
:
{
name
:
string
;
version
:
string
;
logo_uri
?:
string
;
repository
?:
string
;
homepage
?:
string
;
};
instance
:
{
hostname
:
string
;
logo_uri
?:
string
;
banner_uri
?:
string
;
name
?:
string
;
};
};
user
:
{
username
:
string
;
profile
:
string
;
display_name
?:
string
;
picture_url
?:
string
;
};
};
packages/lib/src/renderer/PanZoom.ts
View file @
51eacdaf
...
...
@@ -9,6 +9,7 @@ import {
handleCalculateZoomPositions
,
}
from
"
./lib/zoom.utils
"
;
import
{
Panning
}
from
"
./lib/panning.utils
"
;
import
{
Debug
}
from
"
../debug
"
;
interface
TransformState
{
/**
...
...
@@ -88,8 +89,15 @@ interface ISetup {
// TODO: move these event interfaces out
export
interface
ClickEvent
{
button
:
"
LCLICK
"
|
"
MCLICK
"
|
"
RCLICK
"
;
clientX
:
number
;
clientY
:
number
;
alt
:
boolean
;
ctrl
:
boolean
;
meta
:
boolean
;
shift
:
boolean
;
}
export
interface
HoverEvent
{
...
...
@@ -104,6 +112,7 @@ export interface ViewportMoveEvent {
}
interface
PanZoomEvents
{
longPress
:
(
x
:
number
,
y
:
number
)
=>
void
;
doubleTap
:
(
e
:
TouchEvent
)
=>
void
;
click
:
(
e
:
ClickEvent
)
=>
void
;
hover
:
(
e
:
HoverEvent
)
=>
void
;
...
...
@@ -156,6 +165,8 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
this
.
flags
=
{
useZoom
:
false
,
};
this
.
detectFlags
();
}
initialize
(
...
...
@@ -167,7 +178,6 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
this
.
$zoom
=
$zoom
;
this
.
$move
=
$move
;
this
.
detectFlags
();
this
.
registerMouseEvents
();
this
.
registerTouchEvents
();
...
...
@@ -188,6 +198,51 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
this
.
emit
(
"
initialize
"
);
}
/**
* Get scale that would fit the zoom element in the viewport
* @returns
*/
getZoomToFit
()
{
// https://github.com/BetterTyped/react-zoom-pan-pinch/blob/8dacc2746ca84db22f30275e0745c04aefde5aea/src/core/handlers/handlers.utils.ts#L141
// const wrapperRect = this.$wrapper.getBoundingClientRect();
const
nodeRect
=
this
.
$zoom
.
getBoundingClientRect
();
// const nodeOffset = this._getOffset();
// const nodeLeft = nodeOffset.x;
// const nodeTop = nodeOffset.y;
const
nodeWidth
=
nodeRect
.
width
/
this
.
transform
.
scale
;
const
nodeHeight
=
nodeRect
.
height
/
this
.
transform
.
scale
;
const
scaleX
=
this
.
$wrapper
.
offsetWidth
/
nodeWidth
;
const
scaleY
=
this
.
$wrapper
.
offsetHeight
/
nodeHeight
;
const
newScale
=
Math
.
min
(
scaleX
,
scaleY
);
return
newScale
;
// the following is from the zoomToElement from react-zoom-pan-pinch
// const offsetX = (wrapperRect.width - nodeWidth * newScale) / 2;
// const offsetY = (wrapperRect.height - nodeHeight * newScale) / 2;
// const newPositionX = (wrapperRect.left - nodeLeft) * newScale + offsetX;
// const newPositionY = (wrapperRect.top - nodeTop) * newScale + offsetY;
}
// https://github.com/BetterTyped/react-zoom-pan-pinch/blob/8dacc2746ca84db22f30275e0745c04aefde5aea/src/core/handlers/handlers.utils.ts#L122
_getOffset
()
{
const
wrapperOffset
=
this
.
$wrapper
.
getBoundingClientRect
();
const
contentOffset
=
this
.
$zoom
.
getBoundingClientRect
();
const
xOff
=
wrapperOffset
.
x
*
this
.
transform
.
scale
;
const
yOff
=
wrapperOffset
.
y
*
this
.
transform
.
scale
;
return
{
x
:
(
contentOffset
.
x
+
xOff
)
/
this
.
transform
.
scale
,
y
:
(
contentOffset
.
y
+
yOff
)
/
this
.
transform
.
scale
,
};
}
/**
* Sets transform data
*
...
...
@@ -303,6 +358,8 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
* @param e
*/
private
_touch_touchstart
=
(
event
:
TouchEvent
)
=>
{
event
.
preventDefault
();
const
isDoubleTap
=
this
.
touch
.
lastTouch
&&
+
new
Date
()
-
this
.
touch
.
lastTouch
<
200
;
...
...
@@ -332,7 +389,7 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
* @param e
*/
private
_touch_touchmove
=
(
event
:
TouchEvent
)
=>
{
if
(
this
.
panning
.
enabled
&&
event
.
touches
.
length
===
1
)
{
if
(
this
.
panning
.
active
&&
event
.
touches
.
length
===
1
)
{
event
.
preventDefault
();
event
.
stopPropagation
();
...
...
@@ -351,8 +408,30 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
* @param e
*/
private
_touch_touchend
=
(
event
:
TouchEvent
)
=>
{
if
(
this
.
panning
.
enabled
)
{
this
.
panning
.
enabled
=
false
;
if
(
this
.
touch
.
lastTouch
&&
this
.
panning
.
active
)
{
const
touch
=
event
.
changedTouches
[
0
];
const
dx
=
Math
.
abs
(
this
.
panning
.
x
-
touch
.
clientX
);
const
dy
=
Math
.
abs
(
this
.
panning
.
y
-
touch
.
clientY
);
if
(
Date
.
now
()
-
this
.
touch
.
lastTouch
<
500
&&
dx
<
5
&&
dy
<
5
)
{
this
.
emit
(
"
click
"
,
{
clientX
:
touch
.
clientX
,
clientY
:
touch
.
clientY
,
button
:
"
LCLICK
"
,
alt
:
false
,
shift
:
false
,
ctrl
:
false
,
meta
:
false
,
});
}
if
(
Date
.
now
()
-
this
.
touch
.
lastTouch
>
500
&&
dx
<
25
&&
dy
<
25
)
{
this
.
emit
(
"
longPress
"
,
this
.
panning
.
x
,
this
.
panning
.
y
);
}
}
if
(
this
.
panning
.
active
)
{
this
.
panning
.
active
=
false
;
const
touch
=
event
.
changedTouches
[
0
];
...
...
@@ -370,7 +449,7 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
this
.
touch
.
pinchStartDistance
=
distance
;
this
.
touch
.
lastDistance
=
distance
;
this
.
touch
.
pinchStartScale
=
this
.
transform
.
scale
;
this
.
panning
.
enabled
=
false
;
this
.
panning
.
active
=
false
;
}
onPinch
(
event
:
TouchEvent
)
{
...
...
@@ -410,7 +489,9 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
if
(
newScale
===
scale
)
return
;
// const { x, y } = handleCalculateZoomPositions(
// this returns diff of pixels due to css zoom being used
//
// let { x, y } = handleCalculateZoomPositions(
// this,
// midPoint.x,
// midPoint.y,
...
...
@@ -420,32 +501,39 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
this
.
touch
.
pinchMidpoint
=
midPoint
;
this
.
touch
.
lastDistance
=
currentDistance
;
this
.
debug
(
midPoint
.
x
,
midPoint
.
y
,
"
midpoint
"
);
if
(
Debug
.
flags
.
enabled
(
"
PANZOOM_PINCH_DEBUG_MESSAGES
"
))
{
Debug
.
debug
(
"
point
"
,
midPoint
.
x
,
midPoint
.
y
,
"
midpoint
"
);
Debug
.
debug
(
"
text
"
,
{
scale
:
[
scale
,
newScale
],
x
:
midPoint
.
x
,
y
:
midPoint
.
y
,
tx
:
this
.
transform
.
x
,
ty
:
this
.
transform
.
y
,
xx
:
midPoint
.
x
*
newScale
-
midPoint
.
x
*
scale
,
yy
:
midPoint
.
y
*
newScale
-
midPoint
.
y
*
scale
,
});
}
// TODO: this might be css zoom specific, I have no way to test this
this
.
transform
.
x
=
midPoint
.
x
/
newScale
-
midPoint
.
x
/
scale
;
this
.
transform
.
y
=
midPoint
.
y
/
newScale
-
midPoint
.
x
/
scale
;
if
(
Debug
.
flags
.
enabled
(
"
PANZOOM_PINCH_TRANSFORM_1
"
))
{
this
.
transform
.
x
=
midPoint
.
x
/
newScale
-
midPoint
.
x
/
scale
;
this
.
transform
.
y
=
midPoint
.
y
/
newScale
-
midPoint
.
y
/
scale
;
}
if
(
Debug
.
flags
.
enabled
(
"
PANZOOM_PINCH_TRANSFORM_2
"
))
{
this
.
transform
.
x
=
(
midPoint
.
x
-
this
.
transform
.
x
)
/
(
newScale
-
scale
);
this
.
transform
.
y
=
(
midPoint
.
y
-
this
.
transform
.
y
)
/
(
newScale
-
scale
);
}
this
.
transform
.
scale
=
newScale
;
this
.
update
();
}
debug
(
x
:
number
,
y
:
number
,
id
?:
string
)
{
// if (document.getElementById("debug-" + id)) {
// document.getElementById("debug-" + id)!.style.top = y + "px";
// document.getElementById("debug-" + id)!.style.left = x + "px";
// return;
// }
// let el = document.createElement("div");
// if (id) el.id = "debug-" + id;
// el.classList.add("debug-point");
// el.style.setProperty("top", y + "px");
// el.style.setProperty("left", x + "px");
// document.body.appendChild(el);
}
registerMouseEvents
()
{
console
.
debug
(
"
[PanZoom] Registering mouse events to $wrapper & document
"
);
this
.
$wrapper
.
addEventListener
(
"
contextmenu
"
,
(
e
)
=>
{
e
.
preventDefault
();
});
// zoom
this
.
$wrapper
.
addEventListener
(
"
wheel
"
,
this
.
_mouse_wheel
,
{
passive
:
true
,
...
...
@@ -534,7 +622,9 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
this
.
mouse
.
mouseDown
=
Date
.
now
();
this
.
panning
.
start
(
e
.
clientX
,
e
.
clientY
);
if
(
this
.
panning
.
enabled
)
{
this
.
panning
.
start
(
e
.
clientX
,
e
.
clientY
);
}
};
/**
...
...
@@ -544,19 +634,7 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
* @param e
*/
private
_mouse_mousemove
=
(
e
:
MouseEvent
)
=>
{
if
(
this
.
panning
.
enabled
)
{
e
.
preventDefault
();
e
.
stopPropagation
();
this
.
panning
.
move
(
e
.
clientX
,
e
.
clientY
);
}
else
{
// not panning
this
.
emit
(
"
hover
"
,
{
clientX
:
e
.
clientX
,
clientY
:
e
.
clientY
,
});
}
if
(
this
.
panning
.
enabled
)
{
if
(
this
.
panning
.
active
)
{
e
.
preventDefault
();
e
.
stopPropagation
();
...
...
@@ -590,13 +668,20 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
// difference from the start position to the up position is very very slow,
// so it's most likely intended to be a click
this
.
emit
(
"
click
"
,
{
button
:
[
"
LCLICK
"
,
"
MCLICK
"
,
"
RCLICK
"
][
e
.
button
]
as
any
,
clientX
:
e
.
clientX
,
clientY
:
e
.
clientY
,
alt
:
e
.
altKey
,
ctrl
:
e
.
ctrlKey
,
meta
:
e
.
metaKey
,
shift
:
e
.
shiftKey
,
});
}
}
if
(
this
.
panning
.
enabled
)
{
if
(
this
.
panning
.
active
)
{
// currently panning
e
.
preventDefault
();
e
.
stopPropagation
();
...
...
packages/lib/src/renderer/PanZoomWrapper.tsx
View file @
51eacdaf
...
...
@@ -2,7 +2,19 @@ import React, { useRef, useState, useEffect } from "react";
import
{
RendererContext
}
from
"
./RendererContext
"
;
import
{
PanZoom
}
from
"
./PanZoom
"
;
export
const
PanZoomWrapper
=
({
children
}:
{
children
:
React
.
ReactNode
})
=>
{
export
const
PanZoomWrapper
=
({
children
,
initialPosition
,
}:
{
children
:
React
.
ReactNode
;
initialPosition
?:
(
useCssZoom
:
boolean
)
=>
|
{
x
:
number
;
y
:
number
;
zoom
?:
number
;
}
|
undefined
;
})
=>
{
const
wrapper
=
useRef
<
HTMLDivElement
>
(
null
);
const
zoom
=
useRef
<
HTMLDivElement
>
(
null
);
const
move
=
useRef
<
HTMLDivElement
>
(
null
);
...
...
@@ -15,6 +27,22 @@ export const PanZoomWrapper = ({ children }: { children: React.ReactNode }) => {
const
$move
=
move
.
current
;
if
(
$wrapper
&&
$zoom
&&
$move
)
{
if
(
initialPosition
)
{
const
pos
=
initialPosition
(
instance
.
flags
.
useZoom
);
if
(
pos
)
{
instance
.
setPosition
(
{
x
:
pos
.
x
,
y
:
pos
.
y
,
zoom
:
pos
.
zoom
||
0
,
},
{
suppressEmit
:
true
}
);
}
else
{
console
.
warn
(
"
Failed to set position via initialPosition; no pos
"
);
}
}
instance
.
initialize
(
$wrapper
,
$zoom
,
$move
);
}
...
...
packages/lib/src/renderer/lib/panning.utils.ts
View file @
51eacdaf
...
...
@@ -3,7 +3,8 @@ import { PanZoom } from "../PanZoom";
export
class
Panning
{
private
instance
:
PanZoom
;
public
enabled
:
boolean
=
false
;
public
active
:
boolean
=
false
;
public
enabled
:
boolean
=
true
;
public
x
:
number
=
0
;
public
y
:
number
=
0
;
...
...
@@ -11,13 +12,23 @@ export class Panning {
this
.
instance
=
instance
;
}
public
setEnabled
(
enabled
:
boolean
)
{
// only trigger changes if an actual change was made
if
(
enabled
===
this
.
enabled
)
return
;
this
.
enabled
=
enabled
;
this
.
active
=
false
;
this
.
instance
.
update
();
}
/**
* trigger panning start
* @param x clientX
* @param y clientY
*/
public
start
(
x
:
number
,
y
:
number
)
{
this
.
enabled
=
true
;
this
.
active
=
true
;
this
.
x
=
x
;
this
.
y
=
y
;
}
...
...
@@ -45,7 +56,7 @@ export class Panning {
* @param y clientY
*/
public
end
(
x
:
number
,
y
:
number
)
{
this
.
enabled
=
false
;
this
.
active
=
false
;
const
deltaX
=
(
x
-
this
.
x
)
/
this
.
instance
.
transform
.
scale
;
const
deltaY
=
(
y
-
this
.
y
)
/
this
.
instance
.
transform
.
scale
;
...
...
packages/lib/src/renderer/lib/zoom.utils.ts
View file @
51eacdaf
...
...
@@ -17,7 +17,7 @@ export function handleCalculateZoomPositions(
const
calculatedPositionX
=
x
-
mouseX
*
scaleDifference
;
const
calculatedPositionY
=
y
-
mouseY
*
scaleDifference
;
contextInstance
.
debug
(
calculatedPositionX
,
calculatedPositionY
,
"
zoom
"
);
// Debug.debug("point",
calculatedPositionX, calculatedPositionY, "zoom");
// do not limit to bounds when there is padding animation,
// it causes animation strange behaviour
...
...
packages/server/.env
0 → 100644
View file @
51eacdaf
# DO NOT USE
#
# @prisma/client for some reason will load .env whenever it's imported
# which makes tests impossible to do while this file exists
# every environment variable used in development should be placed in .env.local
#
# https://github.com/prisma/prisma/issues/15620
\ No newline at end of file
packages/server/.eslintignore
0 → 100644
View file @
51eacdaf
dist
\ No newline at end of file
packages/server/.eslintrc.json
View file @
51eacdaf
...
...
@@ -23,7 +23,19 @@
"plugins"
:
[
"@typescript-eslint"
],
"rules"
:
{
"no-console"
:
"error"
,
"no-unused-vars"
:
"off"
,
"@typescript-eslint/no-namespace"
:
"off"
,
"@typescript-eslint/no-explicit-any"
:
"off"
"@typescript-eslint/no-explicit-any"
:
"off"
,
"@typescript-eslint/no-unused-vars"
:
[
"error"
,
{
"argsIgnorePattern"
:
"^_"
,
"caughtErrors"
:
"all"
,
"caughtErrorsIgnorePattern"
:
"^_"
,
"destructuredArrayIgnorePattern"
:
"^_"
,
"varsIgnorePattern"
:
"^_"
,
"ignoreRestSiblings"
:
true
}
]
}
}
packages/server/.gitignore
View file @
51eacdaf
node_modules
# Keep environment variables out of version control
.env
# .env is not actually used
# this file is included as a warning
-.env
packages/server/package.json
View file @
51eacdaf
...
...
@@ -2,11 +2,13 @@
"name"
:
"@sc07-canvas/server"
,
"version"
:
"1.0.0"
,
"scripts"
:
{
"dev"
:
"DOTENV_CONFIG_PATH=.env.local
nodemon
-r dotenv/config src/index.ts"
,
"dev"
:
"DOTENV_CONFIG_PATH=.env.local
tsx watch
-r dotenv/config src/index.ts"
,
"start"
:
"node --enable-source-maps dist/index.js"
,
"profiler"
:
"node --inspect=0.0.0.0:9229 --enable-source-maps dist/index.js"
,
"build"
:
"tsc"
,
"lint"
:
"eslint ."
,
"prisma:studio"
:
"prisma studio"
,
"sentry"
:
"sentry-cli sourcemaps inject dist && SENTRY_RELEASE=`sentry-cli releases propose-version` sentry-cli sourcemaps upload dist"
,
"lint"
:
"eslint --format gitlab ."
,
"prisma:studio"
:
"dotenv -v BROWSER=none -e .env.local -- prisma studio"
,
"prisma:migrate"
:
"prisma migrate deploy"
,
"prisma:seed:palette"
:
"./tool.sh seed_palette"
,
"tool"
:
"./tool.sh"
...
...
@@ -15,28 +17,32 @@
"author"
:
""
,
"license"
:
"ISC"
,
"devDependencies"
:
{
"@tsconfig/recommended"
:
"^1.0.2"
,
"@types/express"
:
"^4.17.17"
,
"@types/express-session"
:
"^1.17.7"
,
"@typescript-eslint/eslint-plugin"
:
"^7.1.0"
,
"@typescript-eslint/parser"
:
"^7.1.0"
,
"dotenv"
:
"^16.3.1"
,
"eslint"
:
"^8.57.0"
,
"nodemon"
:
"^3.0.1"
,
"prettier"
:
"^3.0.1"
,
"prisma"
:
"^5.3.1"
,
"@tsconfig/recommended"
:
"^1.0.8"
,
"@types/cors"
:
"^2.8.17"
,
"@types/express"
:
"^5.0.0"
,
"@types/express-session"
:
"^1.18.1"
,
"@types/uuid"
:
"^10.0.0"
,
"dotenv"
:
"^16.4.7"
,
"prettier"
:
"^3.4.2"
,
"prisma"
:
"^6.1.0"
,
"ts-node"
:
"^10.9.1"
,
"typescript"
:
"^5.
1.6
"
"typescript"
:
"^5.
7.2
"
},
"dependencies"
:
{
"@prisma/client"
:
"^
5.3.1
"
,
"@prisma/client"
:
"^
6.1.0
"
,
"@sc07-canvas/lib"
:
"^1.0.0"
,
"connect-redis"
:
"^7.1.1"
,
"express"
:
"^4.18.2"
,
"express-session"
:
"^1.17.3"
,
"prisma-dbml-generator"
:
"^0.12.0"
,
"redis"
:
"^4.6.12"
,
"socket.io"
:
"^4.7.2"
,
"winston"
:
"^3.11.0"
"@sentry/node"
:
"^8.47.0"
,
"body-parser"
:
"^1.20.2"
,
"connect-redis"
:
"^8.0.1"
,
"cors"
:
"^2.8.5"
,
"express"
:
"^4.21.2"
,
"express-rate-limit"
:
"^7.5.0"
,
"express-session"
:
"^1.18.1"
,
"openid-client"
:
"^6.1.7"
,
"prom-client"
:
"^15.1.3"
,
"rate-limit-redis"
:
"^4.2.0"
,
"redis"
:
"^4.7.0"
,
"socket.io"
:
"^4.8.1"
,
"winston"
:
"^3.17.0"
}
}
packages/server/prisma/dbml/schema.dbml
View file @
51eacdaf
...
...
@@ -2,19 +2,53 @@
//// THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
//// ------------------------------------------------------
Table Setting {
key String [pk]
value String [not null]
}
Table User {
sub String [pk]
lastPixelTime DateTime [default: `now()`, not null]
display_name String
picture_url String
profile_url String
lastTimeGainStarted DateTime [default: `now()`, not null]
pixelStack Int [not null, default: 0]
undoExpires DateTime
isAdmin Boolean [not null, default: false]
isModerator Boolean [not null, default: false]
pixels Pixel [not null]
FactionMember FactionMember [not null]
Ban Ban
AuditLog AuditLog [not null]
IPAddress IPAddress [not null]
}
Table Instance {
id Int [pk, increment]
hostname String [unique, not null]
name String
logo_url String
banner_url String
Ban Ban
}
Table IPAddress {
ip String [not null]
userSub String [not null]
lastUsedAt DateTime [not null]
createdAt DateTime [default: `now()`, not null]
user User [not null]
indexes {
(ip, userSub) [pk]
}
}
Table PaletteColor {
id Int [pk, increment]
name String [not null]
hex String [unique, not null]
pixels Pixel [not null]
}
Table Pixel {
...
...
@@ -23,9 +57,11 @@ Table Pixel {
x Int [not null]
y Int [not null]
color String [not null]
isTop Boolean [not null, default: false]
isModAction Boolean [not null, default: false]
createdAt DateTime [default: `now()`, not null]
deletedAt DateTime
user User [not null]
pallete PaletteColor [not null]
}
Table Faction {
...
...
@@ -80,9 +116,51 @@ Table FactionSettingDefinition {
FactionSetting FactionSetting [not null]
}
Ref: Pixel.userId > User.sub
Table Ban {
id Int [pk, increment]
userId String [unique]
instanceId Int [unique]
privateNote String
publicNote String
expiresAt DateTime [not null]
createdAt DateTime [default: `now()`, not null]
updatedAt DateTime
user User
instance Instance
AuditLog AuditLog [not null]
}
Table AuditLog {
id Int [pk, increment]
userId String
action AuditLogAction [not null]
reason String
comment String
banId Int
createdAt DateTime [default: `now()`, not null]
updatedAt DateTime
user User
ban Ban
}
Enum AuditLogAction {
BAN_CREATE
BAN_UPDATE
BAN_DELETE
CANVAS_SIZE
CANVAS_FILL
CANVAS_FREEZE
CANVAS_UNFREEZE
CANVAS_AREA_UNDO
USER_MOD
USER_UNMOD
USER_ADMIN
USER_UNADMIN
}
Ref: IPAddress.userSub > User.sub
Ref: Pixel.
color > PaletteColor.hex
Ref: Pixel.
userId > User.sub
Ref: FactionMember.sub > User.sub
...
...
@@ -94,4 +172,12 @@ Ref: FactionSocial.factionId > Faction.id
Ref: FactionSetting.key > FactionSettingDefinition.id
Ref: FactionSetting.factionId > Faction.id
\ No newline at end of file
Ref: FactionSetting.factionId > Faction.id
Ref: Ban.userId - User.sub
Ref: Ban.instanceId - Instance.id
Ref: AuditLog.userId > User.sub
Ref: AuditLog.banId > Ban.id
\ No newline at end of file
packages/server/prisma/migrations/20240427225450_add_undo/migration.sql
0 → 100644
View file @
51eacdaf
-- AlterTable
ALTER
TABLE
"User"
ADD
COLUMN
"undoExpires"
TIMESTAMP
(
3
);
packages/server/prisma/migrations/20240529001156_add_roles/migration.sql
0 → 100644
View file @
51eacdaf
-- AlterTable
ALTER
TABLE
"User"
ADD
COLUMN
"isAdmin"
BOOLEAN
NOT
NULL
DEFAULT
false
,
ADD
COLUMN
"isModerator"
BOOLEAN
NOT
NULL
DEFAULT
false
;
packages/server/prisma/migrations/20240530220300_add_settings/migration.sql
0 → 100644
View file @
51eacdaf
-- CreateTable
CREATE
TABLE
"Setting"
(
"key"
TEXT
NOT
NULL
,
"value"
TEXT
NOT
NULL
,
CONSTRAINT
"Setting_pkey"
PRIMARY
KEY
(
"key"
)
);
packages/server/prisma/migrations/20240531195901_add_profile_and_instance_metadata/migration.sql
0 → 100644
View file @
51eacdaf
-- AlterTable
ALTER
TABLE
"User"
ADD
COLUMN
"display_name"
TEXT
,
ADD
COLUMN
"picture_url"
TEXT
,
ADD
COLUMN
"profile_url"
TEXT
;
-- CreateTable
CREATE
TABLE
"Instance"
(
"id"
SERIAL
NOT
NULL
,
"hostname"
TEXT
NOT
NULL
,
"name"
TEXT
,
"logo_url"
TEXT
,
"banner_url"
TEXT
,
CONSTRAINT
"Instance_pkey"
PRIMARY
KEY
(
"id"
)
);
-- CreateIndex
CREATE
UNIQUE
INDEX
"Instance_hostname_key"
ON
"Instance"
(
"hostname"
);
packages/server/prisma/migrations/20240619231750_add_pixel_mod_flag/migration.sql
0 → 100644
View file @
51eacdaf
-- AlterTable
ALTER
TABLE
"Pixel"
ADD
COLUMN
"isModAction"
BOOLEAN
NOT
NULL
DEFAULT
false
;
packages/server/prisma/migrations/20240620192400_is_top_pixel/migration.sql
0 → 100644
View file @
51eacdaf
-- AlterTable
ALTER
TABLE
"Pixel"
ADD
COLUMN
"isTop"
BOOLEAN
NOT
NULL
DEFAULT
false
;
packages/server/prisma/migrations/20240625192346_pixel_deleted_at/migration.sql
0 → 100644
View file @
51eacdaf
-- AlterTable
ALTER
TABLE
"Pixel"
ADD
COLUMN
"deletedAt"
TIMESTAMP
(
3
);
packages/server/prisma/migrations/20240706033830_add_ban_model/migration.sql
0 → 100644
View file @
51eacdaf
-- CreateTable
CREATE
TABLE
"Ban"
(
"id"
SERIAL
NOT
NULL
,
"userId"
TEXT
,
"instanceId"
INTEGER
,
"privateNote"
TEXT
,
"publicNote"
TEXT
,
"expiresAt"
TIMESTAMP
(
3
)
NOT
NULL
,
CONSTRAINT
"Ban_pkey"
PRIMARY
KEY
(
"id"
)
);
-- CreateIndex
CREATE
UNIQUE
INDEX
"Ban_userId_key"
ON
"Ban"
(
"userId"
);
-- CreateIndex
CREATE
UNIQUE
INDEX
"Ban_instanceId_key"
ON
"Ban"
(
"instanceId"
);
-- AddForeignKey
ALTER
TABLE
"Ban"
ADD
CONSTRAINT
"Ban_userId_fkey"
FOREIGN
KEY
(
"userId"
)
REFERENCES
"User"
(
"sub"
)
ON
DELETE
SET
NULL
ON
UPDATE
CASCADE
;
-- AddForeignKey
ALTER
TABLE
"Ban"
ADD
CONSTRAINT
"Ban_instanceId_fkey"
FOREIGN
KEY
(
"instanceId"
)
REFERENCES
"Instance"
(
"id"
)
ON
DELETE
SET
NULL
ON
UPDATE
CASCADE
;
packages/server/prisma/migrations/20240707193624_add_audit_log_model/migration.sql
0 → 100644
View file @
51eacdaf
-- CreateEnum
CREATE
TYPE
"AuditLogAction"
AS
ENUM
(
'BAN_CREATE'
,
'BAN_UPDATE'
,
'BAN_DELETE'
);
-- CreateTable
CREATE
TABLE
"AuditLog"
(
"id"
SERIAL
NOT
NULL
,
"userId"
TEXT
,
"action"
"AuditLogAction"
NOT
NULL
,
"reason"
TEXT
,
"comment"
TEXT
,
"banId"
INTEGER
,
"createdAt"
TIMESTAMP
(
3
)
NOT
NULL
DEFAULT
CURRENT_TIMESTAMP
,
"updatedAt"
TIMESTAMP
(
3
),
CONSTRAINT
"AuditLog_pkey"
PRIMARY
KEY
(
"id"
)
);
-- AddForeignKey
ALTER
TABLE
"AuditLog"
ADD
CONSTRAINT
"AuditLog_userId_fkey"
FOREIGN
KEY
(
"userId"
)
REFERENCES
"User"
(
"sub"
)
ON
DELETE
SET
NULL
ON
UPDATE
CASCADE
;
-- AddForeignKey
ALTER
TABLE
"AuditLog"
ADD
CONSTRAINT
"AuditLog_banId_fkey"
FOREIGN
KEY
(
"banId"
)
REFERENCES
"Ban"
(
"id"
)
ON
DELETE
SET
NULL
ON
UPDATE
CASCADE
;
Prev
1
2
3
4
5
6
7
8
9
Next