mirror of
https://github.com/notherealmarco/WASAPhoto.git
synced 2025-03-14 06:06:15 +01:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
e38f5d08ab
794 changed files with 610205 additions and 1507 deletions
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
|
@ -9,6 +9,7 @@
|
||||||
"type": "go",
|
"type": "go",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mode": "auto",
|
"mode": "auto",
|
||||||
|
"buildFlags": "-tags webui",
|
||||||
"program": "./cmd/webapi"
|
"program": "./cmd/webapi"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -26,7 +26,7 @@ type WebAPIConfiguration struct {
|
||||||
}
|
}
|
||||||
Debug bool
|
Debug bool
|
||||||
DB struct {
|
DB struct {
|
||||||
Filename string `conf:"default:/tmp/decaf.db"`
|
Filename string `conf:"default:./wasaphoto.db"`
|
||||||
}
|
}
|
||||||
Data struct {
|
Data struct {
|
||||||
Path string `conf:"default:/tmp/wasaphoto"`
|
Path string `conf:"default:/tmp/wasaphoto"`
|
||||||
|
|
|
@ -122,11 +122,11 @@ func run() error {
|
||||||
}
|
}
|
||||||
router := apirouter.Handler()
|
router := apirouter.Handler()
|
||||||
|
|
||||||
//router, err = registerWebUI(router)
|
router, err = registerWebUI(router)
|
||||||
//if err != nil {
|
if err != nil {
|
||||||
// logger.WithError(err).Error("error registering web UI handler")
|
logger.WithError(err).Error("error registering web UI handler")
|
||||||
// return fmt.Errorf("registering web UI handler: %w", err)
|
return fmt.Errorf("registering web UI handler: %w", err)
|
||||||
//}
|
}
|
||||||
|
|
||||||
// Apply CORS policy
|
// Apply CORS policy
|
||||||
router = applyCORSHandler(router)
|
router = applyCORSHandler(router)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//go:build !webui
|
// go:build !webui
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,14 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
//"github.com/notherealmarco/WASAPhoto/webui"
|
"github.com/notherealmarco/WASAPhoto/webui"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func registerWebUI(hdl http.Handler) (http.Handler, error) {
|
func registerWebUI(hdl http.Handler) (http.Handler, error) {
|
||||||
|
fmt.Printf("Registering WebUI...")
|
||||||
distDirectory, err := fs.Sub(webui.Dist, "dist")
|
distDirectory, err := fs.Sub(webui.Dist, "dist")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error embedding WebUI dist/ directory: %w", err)
|
return nil, fmt.Errorf("error embedding WebUI dist/ directory: %w", err)
|
||||||
|
|
BIN
cmd/webapi/wasaphoto.db
Normal file
BIN
cmd/webapi/wasaphoto.db
Normal file
Binary file not shown.
336
doc/api.yaml
336
doc/api.yaml
|
@ -4,7 +4,7 @@ info:
|
||||||
description: |-
|
description: |-
|
||||||
Keep in touch with your friends by sharing photos of special moments, thanks to WASAPhoto! You can
|
Keep in touch with your friends by sharing photos of special moments, thanks to WASAPhoto! You can
|
||||||
upload your photos directly from your PC, and they will be visible to everyone following you.
|
upload your photos directly from your PC, and they will be visible to everyone following you.
|
||||||
version: "2.0.0"
|
version: "3.0"
|
||||||
tags:
|
tags:
|
||||||
- name: login
|
- name: login
|
||||||
description: Login API
|
description: Login API
|
||||||
|
@ -37,7 +37,7 @@ paths:
|
||||||
requestBody:
|
requestBody:
|
||||||
$ref: "#/components/requestBodies/userDetails"
|
$ref: "#/components/requestBodies/userDetails"
|
||||||
responses:
|
responses:
|
||||||
'200': #is 201 in the original one
|
'201':
|
||||||
description: User log-in action successful.
|
description: User log-in action successful.
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
|
@ -51,7 +51,7 @@ paths:
|
||||||
tags: ["username"]
|
tags: ["username"]
|
||||||
summary: Updates the username
|
summary: Updates the username
|
||||||
description: Changes the username of the user with the given one.
|
description: Changes the username of the user with the given one.
|
||||||
operationId: setMyUsername
|
operationId: setMyUserName
|
||||||
parameters:
|
parameters:
|
||||||
- name: user_id
|
- name: user_id
|
||||||
in: path
|
in: path
|
||||||
|
@ -73,7 +73,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Username already taken"
|
status: "Username already taken"
|
||||||
'404':
|
'404':
|
||||||
description: The user does not exist.
|
description: The user does not exist.
|
||||||
content:
|
content:
|
||||||
|
@ -81,7 +81,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "User not found"
|
status: "User not found"
|
||||||
|
|
||||||
/users/{user_id}/followers:
|
/users/{user_id}/followers:
|
||||||
get:
|
get:
|
||||||
|
@ -107,6 +107,7 @@ paths:
|
||||||
type: array
|
type: array
|
||||||
description: The list of followers.
|
description: The list of followers.
|
||||||
minItems: 0
|
minItems: 0
|
||||||
|
maxItems: 100
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/uid_name"
|
$ref: "#/components/schemas/uid_name"
|
||||||
example:
|
example:
|
||||||
|
@ -121,7 +122,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "User not found"
|
status: "User not found"
|
||||||
/users/{user_id}/following:
|
/users/{user_id}/following:
|
||||||
get:
|
get:
|
||||||
tags: ["followers"]
|
tags: ["followers"]
|
||||||
|
@ -145,6 +146,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
minItems: 0
|
minItems: 0
|
||||||
|
maxItems: 100
|
||||||
description: The list of users that the user is following.
|
description: The list of users that the user is following.
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/uid_name"
|
$ref: "#/components/schemas/uid_name"
|
||||||
|
@ -160,7 +162,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "User not found"
|
status: "User not found"
|
||||||
|
|
||||||
/users/{user_id}/followers/{follower_uid}:
|
/users/{user_id}/followers/{follower_uid}:
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -184,8 +186,14 @@ paths:
|
||||||
security:
|
security:
|
||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
responses:
|
responses:
|
||||||
'204':
|
'201':
|
||||||
description: Follow user action successful.
|
description: Follow user action successful.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/generic_response"
|
||||||
|
example:
|
||||||
|
status: "Success"
|
||||||
'403':
|
'403':
|
||||||
description: The user has no permission perform this action.
|
description: The user has no permission perform this action.
|
||||||
content:
|
content:
|
||||||
|
@ -193,7 +201,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Forbidden"
|
status: "Forbidden"
|
||||||
'404':
|
'404':
|
||||||
description: The resource does not exist.
|
description: The resource does not exist.
|
||||||
content:
|
content:
|
||||||
|
@ -201,15 +209,15 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "User not found"
|
status: "Resource not found"
|
||||||
'400':
|
'400': # todo: not sure if this is the right error code
|
||||||
description: Trying to follow a user that does not exist.
|
description: Trying to follow a user that does not exist.
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "User not found"
|
status: "User not found"
|
||||||
delete:
|
delete:
|
||||||
tags: ["followers"]
|
tags: ["followers"]
|
||||||
summary: Unfollows a user
|
summary: Unfollows a user
|
||||||
|
@ -227,7 +235,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "User not found"
|
status: "User not found"
|
||||||
|
|
||||||
/users/{user_id}/bans/{ban_uid}:
|
/users/{user_id}/bans/{ban_uid}:
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -251,8 +259,16 @@ paths:
|
||||||
security:
|
security:
|
||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
responses:
|
responses:
|
||||||
'204':
|
'201':
|
||||||
description: Ban user action successful.
|
description: Ban user action successful.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/generic_response"
|
||||||
|
example:
|
||||||
|
status: "Success"
|
||||||
|
'204':
|
||||||
|
description: The user is already banned.
|
||||||
'403':
|
'403':
|
||||||
description: The user has no permission to perform this action.
|
description: The user has no permission to perform this action.
|
||||||
content:
|
content:
|
||||||
|
@ -260,7 +276,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Forbidden"
|
status: "Forbidden"
|
||||||
'404':
|
'404':
|
||||||
description: The user does not exist.
|
description: The user does not exist.
|
||||||
content:
|
content:
|
||||||
|
@ -268,7 +284,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "User not found"
|
status: "User not found"
|
||||||
|
|
||||||
delete:
|
delete:
|
||||||
tags: ["bans"]
|
tags: ["bans"]
|
||||||
|
@ -287,7 +303,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Forbidden"
|
status: "Forbidden"
|
||||||
'404':
|
'404':
|
||||||
description: The user is not banned by the user.
|
description: The user is not banned by the user.
|
||||||
content:
|
content:
|
||||||
|
@ -295,7 +311,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "User not found"
|
status: "User not found"
|
||||||
|
|
||||||
/users/{user_id}/photos/{photo_id}/likes:
|
/users/{user_id}/photos/{photo_id}/likes:
|
||||||
get:
|
get:
|
||||||
|
@ -318,6 +334,18 @@ paths:
|
||||||
$ref: "#/components/schemas/photo_id"
|
$ref: "#/components/schemas/photo_id"
|
||||||
required: true
|
required: true
|
||||||
description: The ID of the photo.
|
description: The ID of the photo.
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/limit"
|
||||||
|
description: The number of elements to show.
|
||||||
|
required: false
|
||||||
|
- name: start_index
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/start_index"
|
||||||
|
description: The starting offset.
|
||||||
|
required: false
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Returns the user list
|
description: Returns the user list
|
||||||
|
@ -326,6 +354,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
minItems: 0
|
minItems: 0
|
||||||
|
maxItems: 100
|
||||||
description: An array of users liking the photo.
|
description: An array of users liking the photo.
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/uid_name"
|
$ref: "#/components/schemas/uid_name"
|
||||||
|
@ -341,7 +370,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Resource not found"
|
status: "Resource not found"
|
||||||
|
|
||||||
/users/{user_id}/photos/{photo_id}/likes/{liker_uid}:
|
/users/{user_id}/photos/{photo_id}/likes/{liker_uid}:
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -371,8 +400,16 @@ paths:
|
||||||
security:
|
security:
|
||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
responses:
|
responses:
|
||||||
'204':
|
'201':
|
||||||
description: Like photo action successful.
|
description: Like photo action successful.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/generic_response"
|
||||||
|
example:
|
||||||
|
status: "Success"
|
||||||
|
'204':
|
||||||
|
description: The user already likes the photo.
|
||||||
'403':
|
'403':
|
||||||
description: The user has no permission to perform this action.
|
description: The user has no permission to perform this action.
|
||||||
content:
|
content:
|
||||||
|
@ -380,7 +417,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Forbidden"
|
status: "Forbidden"
|
||||||
'404':
|
'404':
|
||||||
description: Resource not found (or the author of the photo has banned the authorized user).
|
description: Resource not found (or the author of the photo has banned the authorized user).
|
||||||
content:
|
content:
|
||||||
|
@ -388,7 +425,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Resource not found"
|
status: "Resource not found"
|
||||||
'400':
|
'400':
|
||||||
description: Bad URI (maybe liker_uid is invalid).
|
description: Bad URI (maybe liker_uid is invalid).
|
||||||
content:
|
content:
|
||||||
|
@ -396,7 +433,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Bad photo_id"
|
status: "Bad photo_id"
|
||||||
delete:
|
delete:
|
||||||
tags: ["likes"]
|
tags: ["likes"]
|
||||||
summary: Unlikes a photo
|
summary: Unlikes a photo
|
||||||
|
@ -414,7 +451,75 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Resource not found"
|
status: "Resource not found"
|
||||||
|
/users:
|
||||||
|
get:
|
||||||
|
tags: ["search"]
|
||||||
|
summary: Search users by username
|
||||||
|
description: Search users whose username contains the given string.
|
||||||
|
operationId: searchUsers
|
||||||
|
security:
|
||||||
|
- BearerAuth: []
|
||||||
|
parameters:
|
||||||
|
- name: query
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/name"
|
||||||
|
required: true
|
||||||
|
description: The username to search.
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/limit"
|
||||||
|
description: The number of elements to show.
|
||||||
|
required: false
|
||||||
|
- name: start_index
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/start_index"
|
||||||
|
description: The starting offset.
|
||||||
|
required: false
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Returns the user list
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
minItems: 0
|
||||||
|
maxItems: 100
|
||||||
|
description: An array of users whose username contains the given string.
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/uid_name"
|
||||||
|
example:
|
||||||
|
- user_id: "123e4567-e89b-12d3-a456-426655440000"
|
||||||
|
username: "Maria"
|
||||||
|
- user_id: "123e4567-e89b-12d3-a456-426655440001"
|
||||||
|
username: "Filippo"
|
||||||
|
'404':
|
||||||
|
description: No user found.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/generic_response"
|
||||||
|
example:
|
||||||
|
status: "Resource not found"
|
||||||
|
'400':
|
||||||
|
description: Some parameters are malformed.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/generic_response"
|
||||||
|
example:
|
||||||
|
status: "Invalid start_index or limit value"
|
||||||
|
'401':
|
||||||
|
description: The user is not logged in.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/generic_response"
|
||||||
|
example:
|
||||||
|
status: "Unauthorized"
|
||||||
|
|
||||||
/users/{user_id}:
|
/users/{user_id}:
|
||||||
get:
|
get:
|
||||||
|
@ -442,13 +547,8 @@ paths:
|
||||||
username: "Maria"
|
username: "Maria"
|
||||||
followers: 25
|
followers: 25
|
||||||
following: 32
|
following: 32
|
||||||
photos:
|
followed: true
|
||||||
- photo_id: 2341
|
photos: 50
|
||||||
upload_time: "2020-11-20T12:00:00Z"
|
|
||||||
likes: 2
|
|
||||||
- photo_id: 2342
|
|
||||||
upload_time: "2022-10-23T12:01:00Z"
|
|
||||||
likes: 5
|
|
||||||
'404':
|
'404':
|
||||||
description: User not found (or the authorized user is banned).
|
description: User not found (or the authorized user is banned).
|
||||||
content:
|
content:
|
||||||
|
@ -456,9 +556,61 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Resource not found"
|
status: "Resource not found"
|
||||||
|
|
||||||
/users/{user_id}/photos:
|
/users/{user_id}/photos:
|
||||||
|
parameters:
|
||||||
|
- name: user_id
|
||||||
|
in: path
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/uid"
|
||||||
|
required: true
|
||||||
|
description: The user ID of the user who uploads the photo.
|
||||||
|
|
||||||
|
get:
|
||||||
|
tags: ["photos"]
|
||||||
|
summary: Returns user photos
|
||||||
|
description: Returns the list of photos uploaded by a user.
|
||||||
|
operationId: getUserPhotos
|
||||||
|
security:
|
||||||
|
- BearerAuth: []
|
||||||
|
parameters:
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/limit"
|
||||||
|
description: The number of elements to show.
|
||||||
|
required: false
|
||||||
|
- name: start_index
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/start_index"
|
||||||
|
description: The starting offset.
|
||||||
|
required: false
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Returns the user photos list
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/user_photo_stream"
|
||||||
|
'404':
|
||||||
|
description: User not found (or the authorized user is banned).
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/generic_response"
|
||||||
|
example:
|
||||||
|
status: "Resource not found"
|
||||||
|
'400':
|
||||||
|
description: Some parameters are malformed.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/generic_response"
|
||||||
|
example:
|
||||||
|
status: "Invalid start_index or limit value"
|
||||||
|
|
||||||
post:
|
post:
|
||||||
tags: ["photos"]
|
tags: ["photos"]
|
||||||
summary: Uploads a photo
|
summary: Uploads a photo
|
||||||
|
@ -466,13 +618,6 @@ paths:
|
||||||
operationId: uploadPhoto
|
operationId: uploadPhoto
|
||||||
security:
|
security:
|
||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
parameters:
|
|
||||||
- name: user_id
|
|
||||||
in: path
|
|
||||||
schema:
|
|
||||||
$ref: "#/components/schemas/uid"
|
|
||||||
required: true
|
|
||||||
description: The user ID of the user who uploads the photo.
|
|
||||||
requestBody:
|
requestBody:
|
||||||
content:
|
content:
|
||||||
image/jpeg:
|
image/jpeg:
|
||||||
|
@ -480,6 +625,7 @@ paths:
|
||||||
description: The photo to upload.
|
description: The photo to upload.
|
||||||
type: string
|
type: string
|
||||||
format: binary
|
format: binary
|
||||||
|
pattern: "(?s).*" # todo: review. Btw this means "any string"
|
||||||
responses:
|
responses:
|
||||||
'201':
|
'201':
|
||||||
description: Upload photo action successful.
|
description: Upload photo action successful.
|
||||||
|
@ -496,7 +642,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Resource not found"
|
status: "Resource not found"
|
||||||
|
|
||||||
/users/{user_id}/photos/{photo_id}:
|
/users/{user_id}/photos/{photo_id}:
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -525,7 +671,8 @@ paths:
|
||||||
content:
|
content:
|
||||||
image/jpeg:
|
image/jpeg:
|
||||||
schema:
|
schema:
|
||||||
#todo it wants lenght
|
minLength: 1
|
||||||
|
maxLength: 10485760 # Image should not be bigger than 10 MiB
|
||||||
description: The requested photo in binary format.
|
description: The requested photo in binary format.
|
||||||
type: string
|
type: string
|
||||||
format: binary
|
format: binary
|
||||||
|
@ -536,7 +683,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Resource not found"
|
status: "Resource not found"
|
||||||
delete:
|
delete:
|
||||||
tags: ["photos"]
|
tags: ["photos"]
|
||||||
summary: Deletes a photo
|
summary: Deletes a photo
|
||||||
|
@ -554,7 +701,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Unauthorized"
|
status: "Unauthorized"
|
||||||
'404':
|
'404':
|
||||||
description: User or photo not found.
|
description: User or photo not found.
|
||||||
content:
|
content:
|
||||||
|
@ -562,7 +709,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Resource not found"
|
status: "Resource not found"
|
||||||
|
|
||||||
/users/{user_id}/photos/{photo_id}/comments:
|
/users/{user_id}/photos/{photo_id}/comments:
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -583,6 +730,19 @@ paths:
|
||||||
summary: Gets photo comments
|
summary: Gets photo comments
|
||||||
description: Gets the list of comments of a photo
|
description: Gets the list of comments of a photo
|
||||||
operationId: getPhotoComments
|
operationId: getPhotoComments
|
||||||
|
parameters:
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/limit"
|
||||||
|
description: The number of elements to show.
|
||||||
|
required: false
|
||||||
|
- name: start_index
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/start_index"
|
||||||
|
description: The starting offset.
|
||||||
|
required: false
|
||||||
security:
|
security:
|
||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
responses:
|
responses:
|
||||||
|
@ -610,7 +770,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Resource not found"
|
status: "Resource not found"
|
||||||
post:
|
post:
|
||||||
tags: ["comments"]
|
tags: ["comments"]
|
||||||
summary: Comments a photo
|
summary: Comments a photo
|
||||||
|
@ -627,8 +787,14 @@ paths:
|
||||||
example:
|
example:
|
||||||
comment: "Lovely!"
|
comment: "Lovely!"
|
||||||
responses:
|
responses:
|
||||||
'204':
|
'201':
|
||||||
description: Comment photo action successful. #todo maybe 201
|
description: Comment photo action successful.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/generic_response"
|
||||||
|
example:
|
||||||
|
status: "Created"
|
||||||
'404':
|
'404':
|
||||||
description: The user or the photo does not exists (or the author of the photo has banned the authorized user).
|
description: The user or the photo does not exists (or the author of the photo has banned the authorized user).
|
||||||
content:
|
content:
|
||||||
|
@ -636,14 +802,14 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Resource not found"
|
status: "Resource not found"
|
||||||
|
|
||||||
/users/{user_id}/photos/{photo_id}/comments/{comment_id}:
|
/users/{user_id}/photos/{photo_id}/comments/{comment_id}:
|
||||||
delete:
|
delete:
|
||||||
tags: ["comments"]
|
tags: ["comments"]
|
||||||
summary: Deletes a comment
|
summary: Deletes a comment
|
||||||
description: Deletes a photo in the gallery of the authorized user.
|
description: Deletes a photo in the gallery of the authorized user.
|
||||||
operationId: deleteComment
|
operationId: uncommentPhoto
|
||||||
security:
|
security:
|
||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -675,7 +841,7 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Unauthorized"
|
status: "Unauthorized"
|
||||||
'404':
|
'404':
|
||||||
description: The comment does not exists.
|
description: The comment does not exists.
|
||||||
content:
|
content:
|
||||||
|
@ -683,9 +849,9 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/generic_response"
|
$ref: "#/components/schemas/generic_response"
|
||||||
example:
|
example:
|
||||||
error: "Resource not found"
|
status: "Resource not found"
|
||||||
|
|
||||||
/stream: # todo review path
|
/stream:
|
||||||
get:
|
get:
|
||||||
tags: ["stream"]
|
tags: ["stream"]
|
||||||
summary: Returns user stream
|
summary: Returns user stream
|
||||||
|
@ -697,15 +863,13 @@ paths:
|
||||||
- name: limit
|
- name: limit
|
||||||
in: query
|
in: query
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
$ref: "#/components/schemas/limit"
|
||||||
default: 25
|
|
||||||
description: The number of elements to show.
|
description: The number of elements to show.
|
||||||
required: false
|
required: false
|
||||||
- name: start_index
|
- name: start_index
|
||||||
in: query
|
in: query
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
$ref: "#/components/schemas/start_index"
|
||||||
default: 0
|
|
||||||
description: The starting offset.
|
description: The starting offset.
|
||||||
required: false
|
required: false
|
||||||
responses:
|
responses:
|
||||||
|
@ -717,12 +881,12 @@ paths:
|
||||||
$ref: "#/components/schemas/photo_stream"
|
$ref: "#/components/schemas/photo_stream"
|
||||||
example:
|
example:
|
||||||
- user_id: "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
|
- user_id: "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
|
||||||
- username: "Federicus"
|
username: "Federicus"
|
||||||
- photo_id: 1520
|
photo_id: 157
|
||||||
- upload_time: "2020-11-20T12:00:00Z"
|
upload_time: "2020-11-20T12:00:00Z"
|
||||||
- likes: 93
|
likes: 93
|
||||||
|
comments: 13
|
||||||
|
liked: true
|
||||||
|
|
||||||
components:
|
components:
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
|
@ -731,6 +895,14 @@ components:
|
||||||
scheme: bearer
|
scheme: bearer
|
||||||
|
|
||||||
schemas:
|
schemas:
|
||||||
|
start_index:
|
||||||
|
type: integer
|
||||||
|
description: The starting offset.
|
||||||
|
default: 0
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
description: The number of elements to show.
|
||||||
|
default: 15
|
||||||
uid_name:
|
uid_name:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -775,11 +947,17 @@ components:
|
||||||
upload_time:
|
upload_time:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
description: Upload time and date.
|
description: Upload time and date in RFC3339 format.
|
||||||
|
minLength: 20
|
||||||
|
maxLength: 25
|
||||||
likes:
|
likes:
|
||||||
type: integer
|
type: integer
|
||||||
example: 90
|
example: 90
|
||||||
description: Number of likes.
|
description: Number of likes.
|
||||||
|
comments_n:
|
||||||
|
type: integer
|
||||||
|
example: 7
|
||||||
|
description: Number of comments on the photo.
|
||||||
followers_n:
|
followers_n:
|
||||||
type: integer
|
type: integer
|
||||||
example: 420
|
example: 420
|
||||||
|
@ -788,6 +966,18 @@ components:
|
||||||
type: integer
|
type: integer
|
||||||
example: 69
|
example: 69
|
||||||
description: Number of following users.
|
description: Number of following users.
|
||||||
|
like_status:
|
||||||
|
type: boolean
|
||||||
|
example: true
|
||||||
|
description: Whether the user liked the photo.
|
||||||
|
follow_status:
|
||||||
|
type: boolean
|
||||||
|
example: false
|
||||||
|
description: Whether the user follows the other user.
|
||||||
|
photos_n:
|
||||||
|
type: integer
|
||||||
|
example: 90
|
||||||
|
description: Number of photos.
|
||||||
user_profile:
|
user_profile:
|
||||||
type: object
|
type: object
|
||||||
description: The profile of the user.
|
description: The profile of the user.
|
||||||
|
@ -799,10 +989,13 @@ components:
|
||||||
following:
|
following:
|
||||||
$ref: "#/components/schemas/following_n"
|
$ref: "#/components/schemas/following_n"
|
||||||
photos:
|
photos:
|
||||||
$ref: "#/components/schemas/user_photo_stream"
|
$ref: "#/components/schemas/photos_n"
|
||||||
|
followed:
|
||||||
|
$ref: "#/components/schemas/follow_status"
|
||||||
user_photo_stream:
|
user_photo_stream:
|
||||||
type: array
|
type: array
|
||||||
minItems: 0
|
minItems: 0
|
||||||
|
maxItems: 100
|
||||||
description: An array of photos, upload time and likes.
|
description: An array of photos, upload time and likes.
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/user_photo_stream_item"
|
$ref: "#/components/schemas/user_photo_stream_item"
|
||||||
|
@ -815,9 +1008,14 @@ components:
|
||||||
$ref: "#/components/schemas/upload_time"
|
$ref: "#/components/schemas/upload_time"
|
||||||
likes:
|
likes:
|
||||||
$ref: "#/components/schemas/likes"
|
$ref: "#/components/schemas/likes"
|
||||||
|
comments:
|
||||||
|
$ref: "#/components/schemas/comments_n"
|
||||||
|
liked:
|
||||||
|
$ref: "#/components/schemas/like_status"
|
||||||
photo_stream:
|
photo_stream:
|
||||||
type: array
|
type: array
|
||||||
minItems: 0
|
minItems: 0
|
||||||
|
maxItems: 100
|
||||||
description: An array of photos, author, upload time and likes.
|
description: An array of photos, author, upload time and likes.
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/photo_stream_item"
|
$ref: "#/components/schemas/photo_stream_item"
|
||||||
|
@ -834,9 +1032,14 @@ components:
|
||||||
$ref: "#/components/schemas/upload_time"
|
$ref: "#/components/schemas/upload_time"
|
||||||
likes:
|
likes:
|
||||||
$ref: "#/components/schemas/likes"
|
$ref: "#/components/schemas/likes"
|
||||||
|
comments:
|
||||||
|
$ref: "#/components/schemas/comments_n"
|
||||||
|
liked:
|
||||||
|
$ref: "#/components/schemas/like_status"
|
||||||
comments:
|
comments:
|
||||||
type: array
|
type: array
|
||||||
minItems: 0
|
minItems: 0
|
||||||
|
maxItems: 100
|
||||||
description: An array of comments.
|
description: An array of comments.
|
||||||
items:
|
items:
|
||||||
$ref : "#/components/schemas/comment_item"
|
$ref : "#/components/schemas/comment_item"
|
||||||
|
@ -863,8 +1066,8 @@ components:
|
||||||
$ref: "#/components/schemas/comment"
|
$ref: "#/components/schemas/comment"
|
||||||
comment:
|
comment:
|
||||||
minLength: 5
|
minLength: 5
|
||||||
maxLength: 100 #todo think about it
|
maxLength: 255
|
||||||
pattern: ".*" #everything except newlines
|
pattern: ".*" #everything except newlines ^[*]{5, 255}$
|
||||||
type: string
|
type: string
|
||||||
example: "What a lovely picture! 😊"
|
example: "What a lovely picture! 😊"
|
||||||
description: The comment's text
|
description: The comment's text
|
||||||
|
@ -875,6 +1078,7 @@ components:
|
||||||
properties:
|
properties:
|
||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
|
pattern: ".*"
|
||||||
description: The status of the request.
|
description: The status of the request.
|
||||||
example: "Success"
|
example: "Success"
|
||||||
|
|
||||||
|
|
0
open-npm.sh
Normal file → Executable file
0
open-npm.sh
Normal file → Executable file
|
@ -11,14 +11,18 @@ func (rt *_router) Handler() http.Handler {
|
||||||
|
|
||||||
rt.router.PUT("/users/:user_id/username", rt.wrap(rt.UpdateUsername))
|
rt.router.PUT("/users/:user_id/username", rt.wrap(rt.UpdateUsername))
|
||||||
|
|
||||||
|
rt.router.GET("/users", rt.wrap(rt.GetSearchUsers))
|
||||||
|
|
||||||
rt.router.GET("/users/:user_id/followers", rt.wrap(rt.GetFollowersFollowing))
|
rt.router.GET("/users/:user_id/followers", rt.wrap(rt.GetFollowersFollowing))
|
||||||
rt.router.PUT("/users/:user_id/followers/:follower_uid", rt.wrap(rt.PutFollow))
|
rt.router.PUT("/users/:user_id/followers/:follower_uid", rt.wrap(rt.PutFollow))
|
||||||
rt.router.DELETE("/users/:user_id/followers/:follower_uid", rt.wrap(rt.DeleteFollow))
|
rt.router.DELETE("/users/:user_id/followers/:follower_uid", rt.wrap(rt.DeleteFollow))
|
||||||
rt.router.GET("/users/:user_id/following", rt.wrap(rt.GetFollowersFollowing))
|
rt.router.GET("/users/:user_id/following", rt.wrap(rt.GetFollowersFollowing))
|
||||||
|
|
||||||
|
rt.router.GET("/users/:user_id/bans", rt.wrap(rt.GetUserBans))
|
||||||
rt.router.PUT("/users/:user_id/bans/:ban_uid", rt.wrap(rt.PutBan))
|
rt.router.PUT("/users/:user_id/bans/:ban_uid", rt.wrap(rt.PutBan))
|
||||||
rt.router.DELETE("/users/:user_id/bans/:ban_uid", rt.wrap(rt.DeleteBan))
|
rt.router.DELETE("/users/:user_id/bans/:ban_uid", rt.wrap(rt.DeleteBan))
|
||||||
|
|
||||||
|
rt.router.GET("/users/:user_id/photos", rt.wrap(rt.GetUserPhotos))
|
||||||
rt.router.POST("/users/:user_id/photos", rt.wrap(rt.PostPhoto))
|
rt.router.POST("/users/:user_id/photos", rt.wrap(rt.PostPhoto))
|
||||||
rt.router.GET("/users/:user_id/photos/:photo_id", rt.wrap(rt.GetPhoto))
|
rt.router.GET("/users/:user_id/photos/:photo_id", rt.wrap(rt.GetPhoto))
|
||||||
rt.router.DELETE("/users/:user_id/photos/:photo_id", rt.wrap(rt.DeletePhoto))
|
rt.router.DELETE("/users/:user_id/photos/:photo_id", rt.wrap(rt.DeletePhoto))
|
||||||
|
@ -31,8 +35,9 @@ func (rt *_router) Handler() http.Handler {
|
||||||
rt.router.POST("/users/:user_id/photos/:photo_id/comments", rt.wrap(rt.PostComment))
|
rt.router.POST("/users/:user_id/photos/:photo_id/comments", rt.wrap(rt.PostComment))
|
||||||
rt.router.DELETE("/users/:user_id/photos/:photo_id/comments/:comment_id", rt.wrap(rt.DeleteComment))
|
rt.router.DELETE("/users/:user_id/photos/:photo_id/comments/:comment_id", rt.wrap(rt.DeleteComment))
|
||||||
|
|
||||||
rt.router.GET("/", rt.getHelloWorld)
|
rt.router.GET("/users/:user_id", rt.wrap(rt.GetUserProfile))
|
||||||
rt.router.GET("/context", rt.wrap(rt.getContextReply))
|
|
||||||
|
rt.router.GET("/stream", rt.wrap(rt.GetUserStream))
|
||||||
|
|
||||||
// Special routes
|
// Special routes
|
||||||
rt.router.GET("/liveness", rt.liveness)
|
rt.router.GET("/liveness", rt.liveness)
|
||||||
|
|
|
@ -14,7 +14,7 @@ func BuildAuth(header string) (reqcontext.Authorization, error) {
|
||||||
auth, err := BuildBearer(header)
|
auth, err := BuildBearer(header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "invalid authorization header" {
|
if err.Error() == "invalid authorization header" {
|
||||||
return nil, errors.New("method not supported") // todo: better error description
|
return nil, errors.New("authentication method not supported")
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,25 @@ func SendAuthorizationError(f func(db database.AppDatabase, uid string) (reqcont
|
||||||
}
|
}
|
||||||
// requested user is not found -> 404 as the resource is not found
|
// requested user is not found -> 404 as the resource is not found
|
||||||
if auth == reqcontext.USER_NOT_FOUND {
|
if auth == reqcontext.USER_NOT_FOUND {
|
||||||
helpers.SendStatus(notFoundStatus, w, "Resource not found", l)
|
helpers.SendStatus(notFoundStatus, w, "User not found", l)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SendErrorIfNotLoggedIn(f func(db database.AppDatabase) (reqcontext.AuthStatus, error), db database.AppDatabase, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||||
|
|
||||||
|
auth, err := f(db)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Authorization error", w, l)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if auth == reqcontext.UNAUTHORIZED {
|
||||||
|
helpers.SendStatus(http.StatusUnauthorized, w, "Unauthorized", l)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
@ -10,6 +11,46 @@ import (
|
||||||
"github.com/notherealmarco/WASAPhoto/service/database"
|
"github.com/notherealmarco/WASAPhoto/service/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (rt *_router) GetUserBans(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||||
|
|
||||||
|
// Get user id
|
||||||
|
uid := ps.ByName("user_id")
|
||||||
|
|
||||||
|
if !authorization.SendAuthorizationError(ctx.Auth.UserAuthorized, uid, rt.db, w, rt.baseLogger, http.StatusNotFound) {
|
||||||
|
// A user should not be able to see other users' bans
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get limits, or use default values
|
||||||
|
start_index, limit, err := helpers.GetLimits(r.URL.Query())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// Send error if the limits are specified but invalid
|
||||||
|
helpers.SendBadRequest(w, "Invalid start_index or limit value", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get bans
|
||||||
|
// We don't need to check if the user exists, because the authorization middleware already did that
|
||||||
|
bans, err := rt.db.GetUserBans(uid, start_index, limit)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Database error: GetUserBans", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return ban list
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK) // is it needed?
|
||||||
|
|
||||||
|
err = json.NewEncoder(w).Encode(bans) // write the response
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Error encoding json", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (rt *_router) PutBan(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
func (rt *_router) PutBan(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||||
|
|
||||||
uid := ps.ByName("user_id")
|
uid := ps.ByName("user_id")
|
||||||
|
|
|
@ -3,6 +3,7 @@ package api
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
@ -33,8 +34,16 @@ func (rt *_router) GetComments(w http.ResponseWriter, r *http.Request, ps httpro
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get limits, or use defaults
|
||||||
|
start_index, limit, err := helpers.GetLimits(r.URL.Query())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendBadRequest(w, "Invalid start_index or limit", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// get the user's comments
|
// get the user's comments
|
||||||
success, comments, err := rt.db.GetComments(uid, photo_id)
|
success, comments, err := rt.db.GetComments(uid, photo_id, ctx.Auth.GetUserID(), start_index, limit)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.SendInternalError(err, "Database error: GetComments", w, rt.baseLogger)
|
helpers.SendInternalError(err, "Database error: GetComments", w, rt.baseLogger)
|
||||||
|
@ -73,6 +82,22 @@ func (rt *_router) PostComment(w http.ResponseWriter, r *http.Request, ps httpro
|
||||||
|
|
||||||
// check if the user is authorized to post a comment
|
// check if the user is authorized to post a comment
|
||||||
if !authorization.SendAuthorizationError(ctx.Auth.UserAuthorized, request_body.UID, rt.db, w, rt.baseLogger, http.StatusBadRequest) {
|
if !authorization.SendAuthorizationError(ctx.Auth.UserAuthorized, request_body.UID, rt.db, w, rt.baseLogger, http.StatusBadRequest) {
|
||||||
|
// It returns 400 Bad Request if the user_id field in the request body is missing or an invalid user_id
|
||||||
|
// It returns 401 if the user is not logged in
|
||||||
|
// It returns 403 if the user is not authorized to post a comment as the requested user
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the comment is valid (should not contain newlines and at be between 5 and 255 characters)
|
||||||
|
stat, err := regexp.Match(`^[*]{5, 255}$`, []byte(request_body.Comment))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Error matching regex", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stat {
|
||||||
|
helpers.SendBadRequest(w, "Invalid comment", rt.baseLogger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,10 @@ import (
|
||||||
|
|
||||||
func (rt *_router) GetFollowersFollowing(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
func (rt *_router) GetFollowersFollowing(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||||
|
|
||||||
|
if !authorization.SendErrorIfNotLoggedIn(ctx.Auth.Authorized, rt.db, w, rt.baseLogger) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
uid := ps.ByName("user_id")
|
uid := ps.ByName("user_id")
|
||||||
|
|
||||||
if !helpers.VerifyUserOrNotFound(rt.db, uid, w, rt.baseLogger) {
|
if !helpers.VerifyUserOrNotFound(rt.db, uid, w, rt.baseLogger) {
|
||||||
|
@ -25,13 +29,21 @@ func (rt *_router) GetFollowersFollowing(w http.ResponseWriter, r *http.Request,
|
||||||
var err error
|
var err error
|
||||||
var status database.QueryResult
|
var status database.QueryResult
|
||||||
|
|
||||||
|
// Get limits, or use default values
|
||||||
|
start_index, limit, err := helpers.GetLimits(r.URL.Query())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendBadRequest(w, "Invalid start_index or limit", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Check if client is asking for followers or following
|
// Check if client is asking for followers or following
|
||||||
if strings.HasSuffix(r.URL.Path, "/followers") {
|
if strings.HasSuffix(r.URL.Path, "/followers") {
|
||||||
// Get the followers from the database
|
// Get the followers from the database
|
||||||
status, users, err = rt.db.GetUserFollowers(uid)
|
status, users, err = rt.db.GetUserFollowers(uid, ctx.Auth.GetUserID(), start_index, limit)
|
||||||
} else {
|
} else {
|
||||||
// Get the following users from the database
|
// Get the following users from the database
|
||||||
status, users, err = rt.db.GetUserFollowing(uid)
|
status, users, err = rt.db.GetUserFollowing(uid, ctx.Auth.GetUserID(), start_index, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a 500 response if there was an error
|
// Send a 500 response if there was an error
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
|
|
||||||
"github.com/julienschmidt/httprouter"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// getContextReply is an example of HTTP endpoint that returns "Hello World!" as a plain text. The signature of this
|
|
||||||
// handler accepts a reqcontext.RequestContext (see httpRouterHandler).
|
|
||||||
func (rt *_router) getContextReply(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
|
||||||
w.Header().Set("content-type", "text/plain")
|
|
||||||
_, _ = w.Write([]byte("Hello World!"))
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/julienschmidt/httprouter"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// getHelloWorld is an example of HTTP endpoint that returns "Hello world!" as a plain text
|
|
||||||
func (rt *_router) getHelloWorld(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
|
||||||
w.Header().Set("content-type", "text/plain")
|
|
||||||
_, _ = w.Write([]byte("Hello World!"))
|
|
||||||
}
|
|
48
service/api/get-stream.go
Normal file
48
service/api/get-stream.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/julienschmidt/httprouter"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/api/authorization"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/api/helpers"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (rt *_router) GetUserStream(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||||
|
|
||||||
|
// We must know who is requesting the stream
|
||||||
|
if !authorization.SendErrorIfNotLoggedIn(ctx.Auth.Authorized, rt.db, w, rt.baseLogger) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user id, probably this should be changed as it's not really REST
|
||||||
|
uid := ctx.Auth.GetUserID()
|
||||||
|
|
||||||
|
// Get start index and limit, or their default values
|
||||||
|
start_index, limit, err := helpers.GetLimits(r.URL.Query())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendBadRequest(w, "Invalid start_index or limit value", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the stream
|
||||||
|
stream, err := rt.db.GetUserStream(uid, start_index, limit)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Database error: GetUserProfile", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the stream in json format
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
err = json.NewEncoder(w).Encode(stream)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Error encoding json", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
83
service/api/get-userprofile.go
Normal file
83
service/api/get-userprofile.go
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/julienschmidt/httprouter"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/api/authorization"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/api/helpers"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (rt *_router) GetUserProfile(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||||
|
|
||||||
|
// Get user id
|
||||||
|
uid := ps.ByName("user_id")
|
||||||
|
|
||||||
|
if !authorization.SendErrorIfNotLoggedIn(ctx.Auth.Authorized, rt.db, w, rt.baseLogger) ||
|
||||||
|
!helpers.SendNotFoundIfBanned(rt.db, ctx.Auth.GetUserID(), uid, w, rt.baseLogger) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user profile
|
||||||
|
status, profile, err := rt.db.GetUserProfile(uid, ctx.Auth.GetUserID())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Database error: GetUserProfile", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == database.ERR_NOT_FOUND {
|
||||||
|
helpers.SendNotFound(w, "User not found", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return user profile
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
err = json.NewEncoder(w).Encode(profile)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Error encoding json", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *_router) GetUserPhotos(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||||
|
|
||||||
|
// Get user id
|
||||||
|
uid := ps.ByName("user_id")
|
||||||
|
|
||||||
|
if !authorization.SendErrorIfNotLoggedIn(ctx.Auth.Authorized, rt.db, w, rt.baseLogger) ||
|
||||||
|
!helpers.SendNotFoundIfBanned(rt.db, ctx.Auth.GetUserID(), uid, w, rt.baseLogger) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get limits, or use default values
|
||||||
|
start_index, limit, err := helpers.GetLimits(r.URL.Query())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendBadRequest(w, "Invalid start_index or limit value", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user photos
|
||||||
|
photos, err := rt.db.GetUserPhotos(uid, ctx.Auth.GetUserID(), start_index, limit)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Database error: GetUserPhotos", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return user photos
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
err = json.NewEncoder(w).Encode(photos)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Error encoding json", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,7 +41,6 @@ func SendStatus(httpStatus int, w http.ResponseWriter, description string, l log
|
||||||
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.WithError(err).Error("Error encoding json")
|
l.WithError(err).Error("Error encoding json")
|
||||||
//todo: empty response?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +49,6 @@ func SendNotFound(w http.ResponseWriter, description string, l logrus.FieldLogge
|
||||||
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.WithError(err).Error("Error encoding json")
|
l.WithError(err).Error("Error encoding json")
|
||||||
//todo: empty response?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +57,6 @@ func SendBadRequest(w http.ResponseWriter, description string, l logrus.FieldLog
|
||||||
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.WithError(err).Error("Error encoding json")
|
l.WithError(err).Error("Error encoding json")
|
||||||
//todo: empty response?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +66,6 @@ func SendBadRequestError(err error, description string, w http.ResponseWriter, l
|
||||||
err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.WithError(err).Error("Error encoding json")
|
l.WithError(err).Error("Error encoding json")
|
||||||
//todo: empty response?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +75,6 @@ func SendInternalError(err error, description string, w http.ResponseWriter, l l
|
||||||
err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.WithError(err).Error("Error encoding json")
|
l.WithError(err).Error("Error encoding json")
|
||||||
//todo: empty response?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,3 +84,16 @@ func RollbackOrLogError(tx database.DBTransaction, l logrus.FieldLogger) {
|
||||||
l.WithError(err).Error("Error rolling back transaction")
|
l.WithError(err).Error("Error rolling back transaction")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SendNotFoundIfBanned(db database.AppDatabase, uid string, banner string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||||
|
banned, err := db.IsBanned(uid, banner)
|
||||||
|
if err != nil {
|
||||||
|
SendInternalError(err, "Database error: IsBanned", w, l)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if banned {
|
||||||
|
SendNotFound(w, "User not found", l)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
33
service/api/helpers/get-limits.go
Normal file
33
service/api/helpers/get-limits.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DEFAULT_LIMIT = 15 // don't know if should be moved to config
|
||||||
|
DEFAULT_OFFSET = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetLimits(query url.Values) (int, int, error) {
|
||||||
|
|
||||||
|
limit := DEFAULT_LIMIT
|
||||||
|
start_index := DEFAULT_OFFSET
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if query.Get("limit") != "" {
|
||||||
|
limit, err = strconv.Atoi(query.Get("limit"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if query.Get("start_index") != "" {
|
||||||
|
start_index, err = strconv.Atoi(query.Get("start_index"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return start_index, limit, nil
|
||||||
|
}
|
|
@ -14,6 +14,10 @@ import (
|
||||||
|
|
||||||
func (rt *_router) GetLikes(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
func (rt *_router) GetLikes(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||||
|
|
||||||
|
if !authorization.SendErrorIfNotLoggedIn(ctx.Auth.Authorized, rt.db, w, rt.baseLogger) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// get the user id from the url
|
// get the user id from the url
|
||||||
uid := ps.ByName("user_id")
|
uid := ps.ByName("user_id")
|
||||||
photo_id, err := strconv.ParseInt(ps.ByName("photo_id"), 10, 64)
|
photo_id, err := strconv.ParseInt(ps.ByName("photo_id"), 10, 64)
|
||||||
|
@ -23,13 +27,21 @@ func (rt *_router) GetLikes(w http.ResponseWriter, r *http.Request, ps httproute
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get limits, or use defaults
|
||||||
|
start_index, limit, err := helpers.GetLimits(r.URL.Query())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendBadRequest(w, "Invalid start_index or limit", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// send 404 if the user does not exist
|
// send 404 if the user does not exist
|
||||||
if !helpers.VerifyUserOrNotFound(rt.db, uid, w, rt.baseLogger) {
|
if !helpers.VerifyUserOrNotFound(rt.db, uid, w, rt.baseLogger) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the user's likes
|
// get the user's likes
|
||||||
success, likes, err := rt.db.GetPhotoLikes(uid, photo_id)
|
success, likes, err := rt.db.GetPhotoLikes(uid, photo_id, ctx.Auth.GetUserID(), start_index, limit)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.SendInternalError(err, "Database error: GetLikes", w, rt.baseLogger)
|
helpers.SendInternalError(err, "Database error: GetLikes", w, rt.baseLogger)
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
|
|
||||||
func (rt *_router) PostPhoto(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
func (rt *_router) PostPhoto(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||||
|
|
||||||
defer r.Body.Close()
|
// defer r.Body.Close()
|
||||||
|
|
||||||
uid := ps.ByName("user_id")
|
uid := ps.ByName("user_id")
|
||||||
|
|
||||||
|
@ -70,14 +70,35 @@ func (rt *_router) PostPhoto(w http.ResponseWriter, r *http.Request, ps httprout
|
||||||
|
|
||||||
func (rt *_router) GetPhoto(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
func (rt *_router) GetPhoto(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||||
|
|
||||||
uid := ps.ByName("user_id")
|
if !authorization.SendErrorIfNotLoggedIn(ctx.Auth.Authorized, rt.db, w, rt.baseLogger) {
|
||||||
photo_id := ps.ByName("photo_id")
|
// We want the user to be authenticated
|
||||||
|
|
||||||
if !helpers.VerifyUserOrNotFound(rt.db, uid, w, rt.baseLogger) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
path := rt.dataPath + "/photos/" + uid + "/" + photo_id + ".jpg"
|
uid := ps.ByName("user_id")
|
||||||
|
|
||||||
|
photo_id_str := ps.ByName("photo_id")
|
||||||
|
photo_id, err := strconv.ParseInt(photo_id_str, 10, 64)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendBadRequest(w, "Invalid photo id", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is also checking if the requesting user is banned by the author of the photo
|
||||||
|
exists, err := rt.db.PhotoExists(uid, photo_id, ctx.Auth.GetUserID())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Database error: PhotoExists", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
helpers.SendNotFound(w, "Resource not found", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
path := rt.dataPath + "/photos/" + uid + "/" + photo_id_str + ".jpg"
|
||||||
|
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
|
|
||||||
|
@ -88,7 +109,12 @@ func (rt *_router) GetPhoto(w http.ResponseWriter, r *http.Request, ps httproute
|
||||||
|
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
io.Copy(w, file)
|
_, err = io.Copy(w, file)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Error writing response", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rt *_router) DeletePhoto(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
func (rt *_router) DeletePhoto(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||||
|
@ -113,7 +139,7 @@ func (rt *_router) DeletePhoto(w http.ResponseWriter, r *http.Request, ps httpro
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.SendInternalError(err, "Error deleting photo from database", w, rt.baseLogger)
|
helpers.SendInternalError(err, "Error deleting photo from database", w, rt.baseLogger)
|
||||||
return
|
return
|
||||||
} //todo: maybe let's use a transaction also here
|
} // todo: maybe let's use a transaction also here
|
||||||
|
|
||||||
if !deleted {
|
if !deleted {
|
||||||
helpers.SendNotFound(w, "Photo not found", rt.baseLogger)
|
helpers.SendNotFound(w, "Photo not found", rt.baseLogger)
|
||||||
|
|
|
@ -23,14 +23,13 @@ type _respbody struct {
|
||||||
func (rt *_router) PostSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
func (rt *_router) PostSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||||
|
|
||||||
var request _reqbody
|
var request _reqbody
|
||||||
err := json.NewDecoder(r.Body).Decode(&request) //todo: capire se serve close
|
err := json.NewDecoder(r.Body).Decode(&request)
|
||||||
|
|
||||||
var uid string
|
var uid string
|
||||||
if err == nil { // test if user exists
|
if err == nil { // test if user exists
|
||||||
uid, err = rt.db.GetUserID(request.Name)
|
uid, err = rt.db.GetUserID(request.Name)
|
||||||
}
|
}
|
||||||
if db_errors.EmptySet(err) { // user does not exist
|
if db_errors.EmptySet(err) { // user does not exist
|
||||||
err = nil
|
|
||||||
uid, err = rt.db.CreateUser(request.Name)
|
uid, err = rt.db.CreateUser(request.Name)
|
||||||
}
|
}
|
||||||
if err != nil { // handle any other error
|
if err != nil { // handle any other error
|
||||||
|
|
|
@ -2,11 +2,13 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"github.com/notherealmarco/WASAPhoto/service/api/authorization"
|
"github.com/notherealmarco/WASAPhoto/service/api/authorization"
|
||||||
"github.com/notherealmarco/WASAPhoto/service/api/helpers"
|
"github.com/notherealmarco/WASAPhoto/service/api/helpers"
|
||||||
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
|
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/database"
|
||||||
"github.com/notherealmarco/WASAPhoto/service/structures"
|
"github.com/notherealmarco/WASAPhoto/service/structures"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,19 +23,29 @@ func (rt *_router) UpdateUsername(w http.ResponseWriter, r *http.Request, ps htt
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//err := json.NewDecoder(r.Body).Decode(&req) //todo: capire se serve close
|
stat, err := regexp.Match(`^[a-zA-Z0-9_]{3,16}$`, []byte(req.Name))
|
||||||
|
|
||||||
//if err != nil {
|
|
||||||
// w.WriteHeader(http.StatusBadRequest) // todo: move to DecodeOrBadRequest helper
|
|
||||||
// return
|
|
||||||
//}
|
|
||||||
|
|
||||||
err := rt.db.UpdateUsername(uid, req.Name)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError) // todo: is not ok, maybe let's use a helper
|
helpers.SendInternalError(err, "Error while matching username", w, rt.baseLogger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent) // todo: change to 204 also in API spec
|
if !stat { //todo: sta regex non me piace
|
||||||
|
helpers.SendBadRequest(w, "Username must be between 3 and 16 characters long and can only contain letters, numbers and underscores", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := rt.db.UpdateUsername(uid, req.Name)
|
||||||
|
|
||||||
|
if status == database.ERR_EXISTS {
|
||||||
|
helpers.SendStatus(http.StatusConflict, w, "Username already exists", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Database error: UpdateUsername", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
19
service/api/reqcontext/context-auth.go
Normal file
19
service/api/reqcontext/context-auth.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package reqcontext
|
||||||
|
|
||||||
|
import "github.com/notherealmarco/WASAPhoto/service/database"
|
||||||
|
|
||||||
|
type AuthStatus int
|
||||||
|
|
||||||
|
const (
|
||||||
|
AUTHORIZED = 0
|
||||||
|
UNAUTHORIZED = 1
|
||||||
|
FORBIDDEN = 2
|
||||||
|
USER_NOT_FOUND = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
type Authorization interface {
|
||||||
|
GetType() string
|
||||||
|
GetUserID() string
|
||||||
|
Authorized(db database.AppDatabase) (AuthStatus, error)
|
||||||
|
UserAuthorized(db database.AppDatabase, uid string) (AuthStatus, error)
|
||||||
|
}
|
|
@ -8,19 +8,9 @@ package reqcontext
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
"github.com/notherealmarco/WASAPhoto/service/database"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthStatus int
|
|
||||||
|
|
||||||
const (
|
|
||||||
AUTHORIZED = 0
|
|
||||||
UNAUTHORIZED = 1
|
|
||||||
FORBIDDEN = 2
|
|
||||||
USER_NOT_FOUND = 3
|
|
||||||
) // todo: here?
|
|
||||||
|
|
||||||
// RequestContext is the context of the request, for request-dependent parameters
|
// RequestContext is the context of the request, for request-dependent parameters
|
||||||
type RequestContext struct {
|
type RequestContext struct {
|
||||||
// ReqUUID is the request unique ID
|
// ReqUUID is the request unique ID
|
||||||
|
@ -31,10 +21,3 @@ type RequestContext struct {
|
||||||
|
|
||||||
Auth Authorization
|
Auth Authorization
|
||||||
}
|
}
|
||||||
|
|
||||||
type Authorization interface {
|
|
||||||
GetType() string
|
|
||||||
GetUserID() string
|
|
||||||
Authorized(db database.AppDatabase) (AuthStatus, error)
|
|
||||||
UserAuthorized(db database.AppDatabase, uid string) (AuthStatus, error)
|
|
||||||
}
|
|
||||||
|
|
53
service/api/search.go
Normal file
53
service/api/search.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/julienschmidt/httprouter"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/api/authorization"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/api/helpers"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (rt *_router) GetSearchUsers(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||||
|
|
||||||
|
// We require user to be authenticated
|
||||||
|
if !authorization.SendErrorIfNotLoggedIn(ctx.Auth.Authorized, rt.db, w, rt.baseLogger) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get search query
|
||||||
|
query := r.URL.Query().Get("query")
|
||||||
|
|
||||||
|
if query == "" {
|
||||||
|
helpers.SendBadRequest(w, "Missing query parameter", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get start index and limit, or their default values
|
||||||
|
start_index, limit, err := helpers.GetLimits(r.URL.Query())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendBadRequest(w, "Invalid start_index or limit value", rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get search results
|
||||||
|
results, err := rt.db.SearchByName(query, ctx.Auth.GetUserID(), start_index, limit)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Database error: SearchByName", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the results in json format
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
err = json.NewEncoder(w).Encode(results)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helpers.SendInternalError(err, "Error encoding json", w, rt.baseLogger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,28 +42,36 @@ import (
|
||||||
type AppDatabase interface {
|
type AppDatabase interface {
|
||||||
CreateUser(name string) (string, error)
|
CreateUser(name string) (string, error)
|
||||||
UserExists(uid string) (bool, error)
|
UserExists(uid string) (bool, error)
|
||||||
|
UserExistsNotBanned(uid string, requesting_uid string) (bool, error)
|
||||||
GetUserID(name string) (string, error)
|
GetUserID(name string) (string, error)
|
||||||
|
|
||||||
UpdateUsername(uid, name string) error
|
SearchByName(name string, requesting_uid string, start_index int, limit int) (*[]structures.UIDName, error)
|
||||||
|
|
||||||
GetUserFollowers(uid string) (QueryResult, *[]structures.UIDName, error) // todo: maybe use a pointer to a slice?
|
UpdateUsername(uid string, name string) (QueryResult, error)
|
||||||
GetUserFollowing(uid string) (QueryResult, *[]structures.UIDName, error)
|
|
||||||
|
GetUserFollowers(uid string, requesting_uid string, start_index int, limit int) (QueryResult, *[]structures.UIDName, error)
|
||||||
|
GetUserFollowing(uid string, requesting_uid string, start_index int, offset int) (QueryResult, *[]structures.UIDName, error)
|
||||||
FollowUser(uid string, follow string) (QueryResult, error)
|
FollowUser(uid string, follow string) (QueryResult, error)
|
||||||
UnfollowUser(uid string, unfollow string) (QueryResult, error)
|
UnfollowUser(uid string, unfollow string) (QueryResult, error)
|
||||||
|
|
||||||
BanUser(uid string, ban string) (QueryResult, error)
|
BanUser(uid string, ban string) (QueryResult, error)
|
||||||
UnbanUser(uid string, unban string) (QueryResult, error)
|
UnbanUser(uid string, unban string) (QueryResult, error)
|
||||||
|
IsBanned(uid string, banner string) (bool, error)
|
||||||
|
GetUserBans(uid string, start_index int, limit int) (*[]structures.UIDName, error)
|
||||||
|
|
||||||
PostPhoto(uid string) (DBTransaction, int64, error)
|
PostPhoto(uid string) (DBTransaction, int64, error)
|
||||||
DeletePhoto(uid string, photo int64) (bool, error)
|
DeletePhoto(uid string, photo int64) (bool, error)
|
||||||
|
PhotoExists(uid string, photo int64, requesting_uid string) (bool, error)
|
||||||
|
|
||||||
GetPhotoLikes(uid string, photo int64) (QueryResult, *[]structures.UIDName, error)
|
GetPhotoLikes(uid string, photo int64, requesting_uid string, start_index int, offset int) (QueryResult, *[]structures.UIDName, error)
|
||||||
LikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error)
|
LikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error)
|
||||||
UnlikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error)
|
UnlikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error)
|
||||||
|
|
||||||
GetUserProfile(uid string) (*UserProfile, error)
|
GetUserProfile(uid string, requesting_uid string) (QueryResult, *structures.UserProfile, error)
|
||||||
|
GetUserPhotos(uid string, requesting_uid string, start_index int, limit int) (*[]structures.UserPhoto, error)
|
||||||
|
GetUserStream(uid string, start_index int, limit int) (*[]structures.Photo, error)
|
||||||
|
|
||||||
GetComments(uid string, photo_id int64) (QueryResult, *[]structures.Comment, error)
|
GetComments(uid string, photo_id int64, requesting_uid string, start_index int, offset int) (QueryResult, *[]structures.Comment, error)
|
||||||
PostComment(uid string, photo_id int64, comment_user string, comment string) (QueryResult, error)
|
PostComment(uid string, photo_id int64, comment_user string, comment string) (QueryResult, error)
|
||||||
DeleteComment(uid string, photo_id int64, comment_id int64) (QueryResult, error)
|
DeleteComment(uid string, photo_id int64, comment_id int64) (QueryResult, error)
|
||||||
GetCommentOwner(uid string, photo_id int64, comment_id int64) (QueryResult, string, error)
|
GetCommentOwner(uid string, photo_id int64, comment_id int64) (QueryResult, string, error)
|
||||||
|
|
|
@ -12,7 +12,10 @@ func (db *appdbimpl) PostComment(uid string, photo_id int64, comment_user string
|
||||||
// Check if the photo exists, as API specification requires
|
// Check if the photo exists, as API specification requires
|
||||||
// photos to be identified also by the user who posted them.
|
// photos to be identified also by the user who posted them.
|
||||||
// But our DB implementation only requires the photo id.
|
// But our DB implementation only requires the photo id.
|
||||||
exists, err := db.photoExists(uid, photo_id)
|
//
|
||||||
|
// This also checks if the author has banned the user who is posting the comment
|
||||||
|
// as he should not be able to post comments on his photos
|
||||||
|
exists, err := db.PhotoExists(uid, photo_id, comment_user)
|
||||||
if err != nil || !exists {
|
if err != nil || !exists {
|
||||||
return ERR_NOT_FOUND, err
|
return ERR_NOT_FOUND, err
|
||||||
}
|
}
|
||||||
|
@ -20,8 +23,9 @@ func (db *appdbimpl) PostComment(uid string, photo_id int64, comment_user string
|
||||||
_, err = db.c.Exec(`PRAGMA foreign_keys = ON;
|
_, err = db.c.Exec(`PRAGMA foreign_keys = ON;
|
||||||
INSERT INTO "comments" ("user", "photo", "comment", "date") VALUES (?, ?, ?, ?)`, comment_user, photo_id, comment, time.Now().Format(time.RFC3339))
|
INSERT INTO "comments" ("user", "photo", "comment", "date") VALUES (?, ?, ?, ?)`, comment_user, photo_id, comment, time.Now().Format(time.RFC3339))
|
||||||
|
|
||||||
// todo: we don't actually need it, it's already done before
|
|
||||||
if db_errors.ForeignKeyViolation(err) {
|
if db_errors.ForeignKeyViolation(err) {
|
||||||
|
// trying to post a comment on a photo that does not exist
|
||||||
|
// (actually this should never happen, as we checked if the photo exists before)
|
||||||
return ERR_NOT_FOUND, nil
|
return ERR_NOT_FOUND, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +37,7 @@ func (db *appdbimpl) PostComment(uid string, photo_id int64, comment_user string
|
||||||
|
|
||||||
func (db *appdbimpl) GetCommentOwner(uid string, photo_id int64, comment_id int64) (QueryResult, string, error) {
|
func (db *appdbimpl) GetCommentOwner(uid string, photo_id int64, comment_id int64) (QueryResult, string, error) {
|
||||||
|
|
||||||
// Check if the photo exists, as it exist but have no comments
|
// Check if the photo exists, as it may exist but have no comments
|
||||||
exists, err := db.photoExists(uid, photo_id)
|
exists, err := db.photoExists(uid, photo_id)
|
||||||
if err != nil || !exists {
|
if err != nil || !exists {
|
||||||
return ERR_NOT_FOUND, "", err
|
return ERR_NOT_FOUND, "", err
|
||||||
|
@ -81,32 +85,47 @@ func (db *appdbimpl) DeleteComment(uid string, photo_id int64, comment_id int64)
|
||||||
return SUCCESS, nil
|
return SUCCESS, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *appdbimpl) GetComments(uid string, photo_id int64) (QueryResult, *[]structures.Comment, error) {
|
func (db *appdbimpl) GetComments(uid string, photo_id int64, requesting_uid string, start_index int, limit int) (QueryResult, *[]structures.Comment, error) {
|
||||||
|
|
||||||
// Check if the photo exists, as it exist but have no comments
|
// Check if the photo exists, as it exist but have no comments
|
||||||
exists, err := db.photoExists(uid, photo_id)
|
// this also checks if the author has banned the requesting user
|
||||||
|
exists, err := db.PhotoExists(uid, photo_id, requesting_uid)
|
||||||
if err != nil || !exists {
|
if err != nil || !exists {
|
||||||
return ERR_NOT_FOUND, nil, err
|
return ERR_NOT_FOUND, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := db.c.Query(`SELECT "id", "user", "comment", "date" FROM "comments" WHERE "photo" = ?`, photo_id)
|
rows, err := db.c.Query(`SELECT "c"."id", "c"."user", "c"."comment", "c"."date", "u"."name"
|
||||||
|
FROM "comments" AS "c", "users" AS "u"
|
||||||
|
WHERE "c"."photo" = ?
|
||||||
|
AND "c"."user" NOT IN (
|
||||||
|
SELECT "bans"."user" FROM "bans"
|
||||||
|
WHERE "bans"."user" = "c"."user"
|
||||||
|
AND "bans"."ban" = ?
|
||||||
|
)
|
||||||
|
AND "u"."uid" = "c"."user"
|
||||||
|
LIMIT ?
|
||||||
|
OFFSET ?`, photo_id, requesting_uid, limit, start_index)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ERR_INTERNAL, nil, err
|
return ERR_INTERNAL, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
comments := make([]structures.Comment, 0)
|
comments := make([]structures.Comment, 0)
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var c structures.Comment
|
var c structures.Comment
|
||||||
err = rows.Scan(&c.CommentID, &c.UID, &c.Comment, &c.Date)
|
err = rows.Scan(&c.CommentID, &c.UID, &c.Comment, &c.Date, &c.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ERR_INTERNAL, nil, err
|
return ERR_INTERNAL, nil, err
|
||||||
}
|
}
|
||||||
comments = append(comments, c)
|
comments = append(comments, c)
|
||||||
}
|
}
|
||||||
|
// We check if the iteration ended prematurely
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return ERR_INTERNAL, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return SUCCESS, &comments, nil
|
return SUCCESS, &comments, nil
|
||||||
}
|
}
|
||||||
|
|
118
service/database/db-likes.go
Normal file
118
service/database/db-likes.go
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/database/db_errors"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/structures"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get the list of users who liked a photo
|
||||||
|
func (db *appdbimpl) GetPhotoLikes(uid string, photo int64, requesting_uid string, start_index int, limit int) (QueryResult, *[]structures.UIDName, error) {
|
||||||
|
|
||||||
|
// Check if the photo exists, as it could exist but have no likes.
|
||||||
|
//
|
||||||
|
// This also checks if the author has banned the requesting user
|
||||||
|
// as he should not be able to see anything related to his photos
|
||||||
|
exists, err := db.PhotoExists(uid, photo, requesting_uid)
|
||||||
|
if err != nil {
|
||||||
|
return ERR_INTERNAL, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return ERR_NOT_FOUND, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := db.c.Query(`SELECT "users"."uid", "users"."name" FROM "likes", "users"
|
||||||
|
WHERE "likes"."photo_id" = ?
|
||||||
|
AND "likes"."user" NOT IN (
|
||||||
|
SELECT "bans"."user" FROM "bans"
|
||||||
|
WHERE "bans"."user" = "likes"."user"
|
||||||
|
AND "bans"."ban" = ?
|
||||||
|
)
|
||||||
|
AND "likes"."user" = "users"."uid"
|
||||||
|
LIMIT ?
|
||||||
|
OFFSET ?`, photo, requesting_uid, limit, start_index)
|
||||||
|
if err != nil {
|
||||||
|
return ERR_INTERNAL, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
likes := make([]structures.UIDName, 0)
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var uid string
|
||||||
|
var name string
|
||||||
|
err = rows.Scan(&uid, &name)
|
||||||
|
if err != nil {
|
||||||
|
return ERR_INTERNAL, nil, err
|
||||||
|
}
|
||||||
|
likes = append(likes, structures.UIDName{UID: uid, Name: name})
|
||||||
|
}
|
||||||
|
// We check if the iteration ended prematurely
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return ERR_INTERNAL, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS, &likes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like a photo
|
||||||
|
func (db *appdbimpl) LikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error) {
|
||||||
|
|
||||||
|
// Check if the photo exists, as API specification requires
|
||||||
|
// photos to be identified also by the user who posted them.
|
||||||
|
// But our DB implementation only requires the photo id.
|
||||||
|
//
|
||||||
|
// This also checks if the author of the photo has banned the requesting user
|
||||||
|
// as he should not be able to like his photos
|
||||||
|
exists, err := db.PhotoExists(uid, photo, liker_uid)
|
||||||
|
if err != nil || !exists {
|
||||||
|
return ERR_NOT_FOUND, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.c.Exec(`PRAGMA foreign_keys = ON;
|
||||||
|
INSERT INTO "likes" ("user", "photo_id") VALUES (?, ?)`, liker_uid, photo)
|
||||||
|
|
||||||
|
// The photo exists, but the user already liked it
|
||||||
|
if db_errors.UniqueViolation(err) {
|
||||||
|
return ERR_EXISTS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if db_errors.ForeignKeyViolation(err) {
|
||||||
|
return ERR_NOT_FOUND, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ERR_INTERNAL, err
|
||||||
|
}
|
||||||
|
return SUCCESS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlike a photo
|
||||||
|
func (db *appdbimpl) UnlikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error) {
|
||||||
|
|
||||||
|
// Check if the photo exists, as API specification requires
|
||||||
|
// photos to be identified also by the user who posted them.
|
||||||
|
// But our DB implementation only requires the photo id.
|
||||||
|
exists, err := db.photoExists(uid, photo)
|
||||||
|
if err != nil || !exists {
|
||||||
|
return ERR_NOT_FOUND, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := db.c.Exec(`DELETE FROM "likes" WHERE "user" = ? AND "photo_id" = ?`, liker_uid, photo)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ERR_INTERNAL, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := res.RowsAffected()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ERR_INTERNAL, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows == 0 {
|
||||||
|
return ERR_NOT_FOUND, nil
|
||||||
|
}
|
||||||
|
return SUCCESS, nil
|
||||||
|
}
|
|
@ -1,48 +1,10 @@
|
||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/notherealmarco/WASAPhoto/service/database/db_errors"
|
|
||||||
"github.com/notherealmarco/WASAPhoto/service/structures"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Photo struct {
|
|
||||||
ID int64
|
|
||||||
Likes int64
|
|
||||||
Comments int64
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserProfile struct {
|
|
||||||
UID string
|
|
||||||
Name string
|
|
||||||
Following int64
|
|
||||||
Followers int64
|
|
||||||
Photos []Photo
|
|
||||||
}
|
|
||||||
|
|
||||||
type QueryResult int // todo: move to a separate file
|
|
||||||
|
|
||||||
const (
|
|
||||||
SUCCESS = 0
|
|
||||||
ERR_NOT_FOUND = 1
|
|
||||||
ERR_EXISTS = 2
|
|
||||||
ERR_INTERNAL = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
type dbtransaction struct {
|
|
||||||
c *sql.Tx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx *dbtransaction) Commit() error {
|
|
||||||
return tx.c.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx *dbtransaction) Rollback() error {
|
|
||||||
return tx.c.Rollback()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post a new photo
|
// Post a new photo
|
||||||
func (db *appdbimpl) PostPhoto(uid string) (DBTransaction, int64, error) {
|
func (db *appdbimpl) PostPhoto(uid string) (DBTransaction, int64, error) {
|
||||||
tx, err := db.c.Begin()
|
tx, err := db.c.Begin()
|
||||||
|
@ -53,13 +15,23 @@ func (db *appdbimpl) PostPhoto(uid string) (DBTransaction, int64, error) {
|
||||||
|
|
||||||
res, err := tx.Exec(`INSERT INTO "photos" ("user", "date") VALUES (?, ?)`, uid, time.Now().Format(time.RFC3339))
|
res, err := tx.Exec(`INSERT INTO "photos" ("user", "date") VALUES (?, ?)`, uid, time.Now().Format(time.RFC3339))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback() // error ?
|
err_rb := tx.Rollback()
|
||||||
|
// If rollback fails, we return the original error plus the rollback error
|
||||||
|
if err_rb != nil {
|
||||||
|
err = fmt.Errorf("%w; %w", err, err_rb)
|
||||||
|
}
|
||||||
|
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
id, err := res.LastInsertId()
|
id, err := res.LastInsertId()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback() // error ?
|
err_rb := tx.Rollback()
|
||||||
|
// If rollback fails, we return the original error plus the rollback error
|
||||||
|
if err_rb != nil {
|
||||||
|
err = fmt.Errorf("%w; %w", err, err_rb)
|
||||||
|
}
|
||||||
|
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,52 +53,6 @@ func (db *appdbimpl) DeletePhoto(uid string, photo int64) (bool, error) {
|
||||||
return rows > 0, nil
|
return rows > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user profile, including username, followers, following, and photos
|
|
||||||
func (db *appdbimpl) GetUserProfile(uid string) (*UserProfile, error) {
|
|
||||||
// Get user info
|
|
||||||
var name string
|
|
||||||
err := db.c.QueryRow(`SELECT "name" FROM "users" WHERE "uid" = ?`, uid).Scan(&name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get followers
|
|
||||||
var followers int64
|
|
||||||
err = db.c.QueryRow(`SELECT COUNT(*) FROM "follows" WHERE "followed" = ?`, uid).Scan(&followers)
|
|
||||||
|
|
||||||
// Get following users
|
|
||||||
var following int64
|
|
||||||
err = db.c.QueryRow(`SELECT COUNT(*) FROM "follows" WHERE "follower" = ?`, uid).Scan(&following)
|
|
||||||
|
|
||||||
// Get photos
|
|
||||||
rows, err := db.c.Query(`SELECT "photos"."id", "photos"."date",
|
|
||||||
COUNT("likes"."user") AS "likes",
|
|
||||||
COUNT("comments"."user") AS "comments"
|
|
||||||
FROM "photos", "likes", "comments"
|
|
||||||
WHERE "likes"."photo_id" = "photos"."id"
|
|
||||||
AND "comments"."photo" = "photos"."id"
|
|
||||||
AND "user" = ?`, uid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
photos := make([]Photo, 0)
|
|
||||||
for rows.Next() {
|
|
||||||
var id int64
|
|
||||||
var date string
|
|
||||||
var likes int64
|
|
||||||
var comments int64
|
|
||||||
err = rows.Scan(&id, &date, &likes, &comments)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
photo_data := Photo{id, likes, comments}
|
|
||||||
photos = append(photos, photo_data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &UserProfile{uid, name, followers, following, photos}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a given photo owned by a given user exists
|
// Check if a given photo owned by a given user exists
|
||||||
func (db *appdbimpl) photoExists(uid string, photo int64) (bool, error) {
|
func (db *appdbimpl) photoExists(uid string, photo int64) (bool, error) {
|
||||||
|
|
||||||
|
@ -138,94 +64,19 @@ func (db *appdbimpl) photoExists(uid string, photo int64) (bool, error) {
|
||||||
return cnt > 0, nil
|
return cnt > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the list of users who liked a photo
|
func (db *appdbimpl) PhotoExists(uid string, photo int64, requesting_uid string) (bool, error) {
|
||||||
func (db *appdbimpl) GetPhotoLikes(uid string, photo int64) (QueryResult, *[]structures.UIDName, error) {
|
|
||||||
|
|
||||||
// Check if the photo exists, as it could exist but have no likes
|
var cnt int64
|
||||||
exists, err := db.photoExists(uid, photo)
|
err := db.c.QueryRow(`SELECT COUNT(*) FROM "photos"
|
||||||
|
WHERE "id" = ?
|
||||||
|
AND "user" = ?
|
||||||
|
AND "user" NOT IN (
|
||||||
|
SELECT "bans"."user" FROM "bans"
|
||||||
|
WHERE "bans"."user" = "photos"."user"
|
||||||
|
AND "bans"."ban" = ?
|
||||||
|
)`, photo, uid, requesting_uid).Scan(&cnt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ERR_INTERNAL, nil, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
return cnt > 0, nil
|
||||||
if !exists {
|
|
||||||
return ERR_NOT_FOUND, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := db.c.Query(`SELECT "users"."uid", "users"."name" FROM "likes", "users"
|
|
||||||
WHERE "likes"."photo_id" = ?
|
|
||||||
AND "likes"."user" = "users"."uid"`, photo)
|
|
||||||
if err != nil {
|
|
||||||
return ERR_INTERNAL, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
likes := make([]structures.UIDName, 0)
|
|
||||||
for rows.Next() {
|
|
||||||
var uid string
|
|
||||||
var name string
|
|
||||||
err = rows.Scan(&uid, &name)
|
|
||||||
if err != nil {
|
|
||||||
return ERR_INTERNAL, nil, err
|
|
||||||
}
|
|
||||||
likes = append(likes, structures.UIDName{UID: uid, Name: name})
|
|
||||||
}
|
|
||||||
|
|
||||||
return SUCCESS, &likes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Like a photo
|
|
||||||
func (db *appdbimpl) LikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error) {
|
|
||||||
|
|
||||||
// Check if the photo exists, as API specification requires
|
|
||||||
// photos to be identified also by the user who posted them.
|
|
||||||
// But our DB implementation only requires the photo id.
|
|
||||||
exists, err := db.photoExists(uid, photo)
|
|
||||||
if err != nil || !exists {
|
|
||||||
return ERR_NOT_FOUND, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.c.Exec(`PRAGMA foreign_keys = ON;
|
|
||||||
INSERT INTO "likes" ("user", "photo_id") VALUES (?, ?)`, liker_uid, photo)
|
|
||||||
|
|
||||||
// The photo exists, but the user already liked it
|
|
||||||
if db_errors.UniqueViolation(err) {
|
|
||||||
return ERR_EXISTS, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if db_errors.ForeignKeyViolation(err) {
|
|
||||||
return ERR_NOT_FOUND, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return ERR_INTERNAL, err
|
|
||||||
}
|
|
||||||
return SUCCESS, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlike a photo
|
|
||||||
func (db *appdbimpl) UnlikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error) {
|
|
||||||
|
|
||||||
// Check if the photo exists, as API specification requires
|
|
||||||
// photos to be identified also by the user who posted them.
|
|
||||||
// But our DB implementation only requires the photo id.
|
|
||||||
exists, err := db.photoExists(uid, photo)
|
|
||||||
if err != nil || !exists {
|
|
||||||
return ERR_NOT_FOUND, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := db.c.Exec(`DELETE FROM "likes" WHERE "user" = ? AND "photo_id" = ?`, liker_uid, photo)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return ERR_INTERNAL, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := res.RowsAffected()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return ERR_INTERNAL, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if rows == 0 {
|
|
||||||
return ERR_NOT_FOUND, nil
|
|
||||||
}
|
|
||||||
return SUCCESS, nil
|
|
||||||
}
|
}
|
||||||
|
|
114
service/database/db-profile.go
Normal file
114
service/database/db-profile.go
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/database/db_errors"
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/structures"
|
||||||
|
)
|
||||||
|
|
||||||
|
//this should be changed, but we need to change OpenAPI first
|
||||||
|
|
||||||
|
// Get user profile, including username, followers, following, and photos
|
||||||
|
func (db *appdbimpl) GetUserProfile(uid string, requesting_uid string) (QueryResult, *structures.UserProfile, error) {
|
||||||
|
// Get user info
|
||||||
|
var name string
|
||||||
|
err := db.c.QueryRow(`SELECT "name" FROM "users" WHERE "uid" = ?`, uid).Scan(&name)
|
||||||
|
|
||||||
|
if db_errors.EmptySet(err) {
|
||||||
|
// Query returned no rows, the user does not exist
|
||||||
|
return ERR_NOT_FOUND, nil, nil
|
||||||
|
|
||||||
|
} else if err != nil {
|
||||||
|
return ERR_INTERNAL, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get followers
|
||||||
|
var followers int64
|
||||||
|
err = db.c.QueryRow(`SELECT COUNT(*) FROM "follows" WHERE "followed" = ?`, uid).Scan(&followers)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// Return the error
|
||||||
|
return ERR_INTERNAL, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get following users
|
||||||
|
var following int64
|
||||||
|
err = db.c.QueryRow(`SELECT COUNT(*) FROM "follows" WHERE "follower" = ?`, uid).Scan(&following)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// Return the error
|
||||||
|
return ERR_INTERNAL, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var photos int64
|
||||||
|
err = db.c.QueryRow(`SELECT COUNT(*) FROM "photos" WHERE "photos"."user" = ?`, uid).Scan(&photos)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ERR_INTERNAL, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get follow status
|
||||||
|
var follow_status bool
|
||||||
|
err = db.c.QueryRow(`SELECT EXISTS (SELECT * FROM "follows" WHERE "follower" = ? AND "followed" = ?)`, requesting_uid, uid).Scan(&follow_status)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ERR_INTERNAL, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS, &structures.UserProfile{
|
||||||
|
UID: uid,
|
||||||
|
Name: name,
|
||||||
|
Following: following,
|
||||||
|
Followers: followers,
|
||||||
|
Followed: follow_status,
|
||||||
|
Photos: photos,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *appdbimpl) GetUserPhotos(uid string, requesting_uid string, start_index int, limit int) (*[]structures.UserPhoto, error) {
|
||||||
|
|
||||||
|
// Get photos
|
||||||
|
rows, err := db.c.Query(`SELECT "p"."id", "p"."date",
|
||||||
|
(
|
||||||
|
SELECT COUNT(*) AS "likes" FROM "likes" AS "l"
|
||||||
|
WHERE "l"."photo_id" = "p"."id"
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SELECT COUNT(*) AS "comments" FROM "comments" AS "c"
|
||||||
|
WHERE "c"."photo" = "p"."id"
|
||||||
|
),
|
||||||
|
EXISTS (
|
||||||
|
SELECT * FROM "likes" AS "l"
|
||||||
|
WHERE "l"."photo_id" = "p"."id"
|
||||||
|
AND "l"."user" = ?
|
||||||
|
)
|
||||||
|
FROM "photos" AS "p"
|
||||||
|
WHERE "p"."user" = ?
|
||||||
|
|
||||||
|
LIMIT ?
|
||||||
|
OFFSET ?`, requesting_uid, uid, limit, start_index)
|
||||||
|
if err != nil {
|
||||||
|
// Return the error
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
photos := make([]structures.UserPhoto, 0)
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
// If there is a next row, we create an instance of Photo and add it to the slice
|
||||||
|
var photo structures.UserPhoto
|
||||||
|
err = rows.Scan(&photo.ID, &photo.Date, &photo.Likes, &photo.Comments, &photo.Liked)
|
||||||
|
if err != nil {
|
||||||
|
// Return the error
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
photos = append(photos, photo)
|
||||||
|
}
|
||||||
|
// We check if the iteration ended prematurely
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &photos, nil
|
||||||
|
}
|
11
service/database/db-results.go
Normal file
11
service/database/db-results.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
type QueryResult int
|
||||||
|
|
||||||
|
// Constants used to represent the result of queries
|
||||||
|
const (
|
||||||
|
SUCCESS = 0
|
||||||
|
ERR_NOT_FOUND = 1
|
||||||
|
ERR_EXISTS = 2
|
||||||
|
ERR_INTERNAL = 3
|
||||||
|
)
|
55
service/database/db-stream.go
Normal file
55
service/database/db-stream.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/notherealmarco/WASAPhoto/service/structures"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get user stream
|
||||||
|
func (db *appdbimpl) GetUserStream(uid string, start_index int, limit int) (*[]structures.Photo, error) {
|
||||||
|
|
||||||
|
// Get photos from the database
|
||||||
|
rows, err := db.c.Query(`SELECT "p"."user", "p"."id", "p"."date",
|
||||||
|
(
|
||||||
|
SELECT COUNT(*) AS "likes" FROM "likes" AS "l"
|
||||||
|
WHERE "l"."photo_id" = "p"."id"
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SELECT COUNT(*) AS "comments" FROM "comments" AS "c"
|
||||||
|
WHERE "c"."photo" = "p"."id"
|
||||||
|
)
|
||||||
|
FROM "photos" AS "p"
|
||||||
|
WHERE "p"."user" IN (
|
||||||
|
SELECT "followed" FROM "follows" WHERE "follower" = ?
|
||||||
|
)
|
||||||
|
AND "p"."user" NOT IN (
|
||||||
|
SELECT "user" FROM "bans" WHERE "ban" = ?
|
||||||
|
)
|
||||||
|
ORDER BY "p"."date" DESC
|
||||||
|
LIMIT ?
|
||||||
|
OFFSET ?`, uid, uid, limit, start_index)
|
||||||
|
if err != nil {
|
||||||
|
// Return the error
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
photos := make([]structures.Photo, 0)
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
// If there is a next row, we create an instance of Photo and add it to the slice
|
||||||
|
var photo structures.Photo
|
||||||
|
err = rows.Scan(&photo.UID, &photo.ID, &photo.Date, &photo.Likes, &photo.Comments)
|
||||||
|
if err != nil {
|
||||||
|
// Return the error
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
photos = append(photos, photo)
|
||||||
|
}
|
||||||
|
// We check if the iteration ended prematurely
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &photos, nil
|
||||||
|
}
|
15
service/database/db-transactions.go
Normal file
15
service/database/db-transactions.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import "database/sql"
|
||||||
|
|
||||||
|
type dbtransaction struct {
|
||||||
|
c *sql.Tx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *dbtransaction) Commit() error {
|
||||||
|
return tx.c.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *dbtransaction) Rollback() error {
|
||||||
|
return tx.c.Rollback()
|
||||||
|
}
|
|
@ -8,20 +8,34 @@ import (
|
||||||
"github.com/notherealmarco/WASAPhoto/service/structures"
|
"github.com/notherealmarco/WASAPhoto/service/structures"
|
||||||
)
|
)
|
||||||
|
|
||||||
//Check if user exists and if exists return the user id by username
|
|
||||||
//todo
|
|
||||||
|
|
||||||
// Check if user exists
|
// Check if user exists
|
||||||
func (db *appdbimpl) UserExists(uid string) (bool, error) {
|
func (db *appdbimpl) UserExists(uid string) (bool, error) {
|
||||||
var name string
|
|
||||||
err := db.c.QueryRow(`SELECT "name" FROM "users" WHERE "uid" = ?`, uid).Scan(&name)
|
|
||||||
|
|
||||||
if db_errors.EmptySet(err) {
|
var cnt int
|
||||||
return false, nil
|
err := db.c.QueryRow(`SELECT COUNT(*) FROM "users" WHERE "uid" = ?`, uid).Scan(&cnt)
|
||||||
} else if err != nil {
|
|
||||||
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return true, nil
|
return cnt > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// User exists and is not banned
|
||||||
|
func (db *appdbimpl) UserExistsNotBanned(uid string, requesting_uid string) (bool, error) {
|
||||||
|
|
||||||
|
var cnt int
|
||||||
|
err := db.c.QueryRow(`SELECT COUNT(*) FROM "users"
|
||||||
|
WHERE "uid" = ?
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT "bans"."user" FROM "bans"
|
||||||
|
WHERE "bans"."user" = "users"."uid"
|
||||||
|
AND "bans"."ban" = ?
|
||||||
|
)`, uid, requesting_uid).Scan(&cnt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return cnt > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user id by username
|
// Get user id by username
|
||||||
|
@ -42,16 +56,25 @@ func (db *appdbimpl) CreateUser(name string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update username
|
// Update username
|
||||||
func (db *appdbimpl) UpdateUsername(uid string, name string) error {
|
func (db *appdbimpl) UpdateUsername(uid string, name string) (QueryResult, error) {
|
||||||
_, err := db.c.Exec(`UPDATE "users" SET "name" = ? WHERE "uid" = ?`, name, uid)
|
_, err := db.c.Exec(`UPDATE "users" SET "name" = ? WHERE "uid" = ?`, name, uid)
|
||||||
return err
|
|
||||||
|
if db_errors.UniqueViolation(err) {
|
||||||
|
return ERR_EXISTS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ERR_INTERNAL, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user followers
|
// Get user followers
|
||||||
func (db *appdbimpl) GetUserFollowers(uid string) (QueryResult, *[]structures.UIDName, error) {
|
func (db *appdbimpl) GetUserFollowers(uid string, requesting_uid string, start_index int, limit int) (QueryResult, *[]structures.UIDName, error) {
|
||||||
|
|
||||||
// user may exist but have no followers
|
// user may exist but have no followers
|
||||||
exists, err := db.UserExists(uid)
|
exists, err := db.UserExistsNotBanned(uid, requesting_uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ERR_INTERNAL, nil, err
|
return ERR_INTERNAL, nil, err
|
||||||
|
@ -61,9 +84,18 @@ func (db *appdbimpl) GetUserFollowers(uid string) (QueryResult, *[]structures.UI
|
||||||
return ERR_NOT_FOUND, nil, nil
|
return ERR_NOT_FOUND, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := db.c.Query(`SELECT "follower", "user"."name" FROM "follows", "users"
|
rows, err := db.c.Query(`SELECT "follower", "users"."name" FROM "follows", "users"
|
||||||
WHERE "follows"."follower" = "users"."uid"
|
WHERE "follows"."follower" = "users"."uid"
|
||||||
AND "followed" = ?`, uid)
|
|
||||||
|
AND "follows"."follower" NOT IN (
|
||||||
|
SELECT "bans"."user" FROM "bans"
|
||||||
|
WHERE "bans"."user" = "follows"."follower"
|
||||||
|
AND "bans"."ban" = ?
|
||||||
|
)
|
||||||
|
|
||||||
|
AND "followed" = ?
|
||||||
|
LIMIT ?
|
||||||
|
OFFSET ?`, uid, requesting_uid, limit, start_index)
|
||||||
|
|
||||||
followers, err := db.uidNameQuery(rows, err)
|
followers, err := db.uidNameQuery(rows, err)
|
||||||
|
|
||||||
|
@ -75,10 +107,10 @@ func (db *appdbimpl) GetUserFollowers(uid string) (QueryResult, *[]structures.UI
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user following
|
// Get user following
|
||||||
func (db *appdbimpl) GetUserFollowing(uid string) (QueryResult, *[]structures.UIDName, error) {
|
func (db *appdbimpl) GetUserFollowing(uid string, requesting_uid string, start_index int, offset int) (QueryResult, *[]structures.UIDName, error) {
|
||||||
|
|
||||||
// user may exist but have no followers
|
// user may exist but have no followers
|
||||||
exists, err := db.UserExists(uid)
|
exists, err := db.UserExistsNotBanned(uid, requesting_uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ERR_INTERNAL, nil, err
|
return ERR_INTERNAL, nil, err
|
||||||
|
@ -90,7 +122,16 @@ func (db *appdbimpl) GetUserFollowing(uid string) (QueryResult, *[]structures.UI
|
||||||
|
|
||||||
rows, err := db.c.Query(`SELECT "followed", "user"."name" FROM "follows", "users"
|
rows, err := db.c.Query(`SELECT "followed", "user"."name" FROM "follows", "users"
|
||||||
WHERE "follows"."followed" = "users"."uid"
|
WHERE "follows"."followed" = "users"."uid"
|
||||||
AND "follower" = ?`, uid)
|
|
||||||
|
AND "follows"."followed" NOT IN (
|
||||||
|
SELECT "bans"."user" FROM "bans"
|
||||||
|
WHERE "bans"."user" = "follows"."followed"
|
||||||
|
AND "bans"."ban" = ?
|
||||||
|
)
|
||||||
|
|
||||||
|
AND "follower" = ?
|
||||||
|
LIMIT ?
|
||||||
|
OFFSET ?`, uid, requesting_uid, offset, start_index)
|
||||||
|
|
||||||
following, err := db.uidNameQuery(rows, err)
|
following, err := db.uidNameQuery(rows, err)
|
||||||
|
|
||||||
|
@ -109,6 +150,9 @@ func (db *appdbimpl) uidNameQuery(rows *sql.Rows, err error) (*[]structures.UIDN
|
||||||
}
|
}
|
||||||
|
|
||||||
var followers []structures.UIDName = make([]structures.UIDName, 0)
|
var followers []structures.UIDName = make([]structures.UIDName, 0)
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var uid string
|
var uid string
|
||||||
var name string
|
var name string
|
||||||
|
@ -118,6 +162,11 @@ func (db *appdbimpl) uidNameQuery(rows *sql.Rows, err error) (*[]structures.UIDN
|
||||||
}
|
}
|
||||||
followers = append(followers, structures.UIDName{UID: uid, Name: name})
|
followers = append(followers, structures.UIDName{UID: uid, Name: name})
|
||||||
}
|
}
|
||||||
|
// We check if the iteration ended prematurely
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &followers, nil
|
return &followers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,3 +252,56 @@ func (db *appdbimpl) UnbanUser(uid string, unban string) (QueryResult, error) {
|
||||||
}
|
}
|
||||||
return SUCCESS, nil
|
return SUCCESS, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is user banned by another user
|
||||||
|
func (db *appdbimpl) IsBanned(uid string, banner string) (bool, error) {
|
||||||
|
|
||||||
|
var cnt int
|
||||||
|
err := db.c.QueryRow(`SELECT COUNT(*) FROM "bans" WHERE "user" = ? AND "ban" = ?`, banner, uid).Scan(&cnt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *appdbimpl) GetUserBans(uid string, start_index int, limit int) (*[]structures.UIDName, error) {
|
||||||
|
|
||||||
|
rows, err := db.c.Query(`SELECT "ban", "users"."name" FROM "bans", "users"
|
||||||
|
WHERE "bans"."ban" = "users"."uid"
|
||||||
|
AND "bans"."user" = ?
|
||||||
|
LIMIT ?
|
||||||
|
OFFSET ?`, uid, limit, start_index)
|
||||||
|
|
||||||
|
bans, err := db.uidNameQuery(rows, err)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bans, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search by name
|
||||||
|
func (db *appdbimpl) SearchByName(name string, requesting_uid string, start_index int, limit int) (*[]structures.UIDName, error) {
|
||||||
|
|
||||||
|
rows, err := db.c.Query(`SELECT "uid", "name" FROM "users"
|
||||||
|
WHERE "name" LIKE '%' || ? || '%'
|
||||||
|
|
||||||
|
AND "uid" NOT IN (
|
||||||
|
SELECT "bans"."user" FROM "bans"
|
||||||
|
WHERE "bans"."user" = "users"."uid"
|
||||||
|
AND "bans"."ban" = ?
|
||||||
|
)
|
||||||
|
LIMIT ?
|
||||||
|
OFFSET ?`, name, requesting_uid, limit, start_index)
|
||||||
|
|
||||||
|
users, err := db.uidNameQuery(rows, err)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
|
@ -20,3 +20,29 @@ type Comment struct {
|
||||||
Comment string `json:"comment"`
|
Comment string `json:"comment"`
|
||||||
Date string `json:"date"`
|
Date string `json:"date"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Photo struct {
|
||||||
|
UID string `json:"user_id"`
|
||||||
|
ID int64 `json:"photo_id"`
|
||||||
|
Likes int64 `json:"likes"`
|
||||||
|
Comments int64 `json:"comments"`
|
||||||
|
Date string `json:"date"`
|
||||||
|
Liked bool `json:"liked"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserPhoto struct {
|
||||||
|
ID int64 `json:"photo_id"`
|
||||||
|
Likes int64 `json:"likes"`
|
||||||
|
Comments int64 `json:"comments"`
|
||||||
|
Date string `json:"date"`
|
||||||
|
Liked bool `json:"liked"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserProfile struct {
|
||||||
|
UID string `json:"user_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Following int64 `json:"following"`
|
||||||
|
Followers int64 `json:"followers"`
|
||||||
|
Followed bool `json:"followed"`
|
||||||
|
Photos int64 `json:"photos"`
|
||||||
|
}
|
||||||
|
|
9
vendor/github.com/google/uuid/.travis.yml
generated
vendored
9
vendor/github.com/google/uuid/.travis.yml
generated
vendored
|
@ -1,9 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.4.3
|
|
||||||
- 1.5.3
|
|
||||||
- tip
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test -v ./...
|
|
10
vendor/github.com/google/uuid/CONTRIBUTING.md
generated
vendored
10
vendor/github.com/google/uuid/CONTRIBUTING.md
generated
vendored
|
@ -1,10 +0,0 @@
|
||||||
# How to contribute
|
|
||||||
|
|
||||||
We definitely welcome patches and contribution to this project!
|
|
||||||
|
|
||||||
### Legal requirements
|
|
||||||
|
|
||||||
In order to protect both you and ourselves, you will need to sign the
|
|
||||||
[Contributor License Agreement](https://cla.developers.google.com/clas).
|
|
||||||
|
|
||||||
You may have already signed it for other Google projects.
|
|
9
vendor/github.com/google/uuid/CONTRIBUTORS
generated
vendored
9
vendor/github.com/google/uuid/CONTRIBUTORS
generated
vendored
|
@ -1,9 +0,0 @@
|
||||||
Paul Borman <borman@google.com>
|
|
||||||
bmatsuo
|
|
||||||
shawnps
|
|
||||||
theory
|
|
||||||
jboverfelt
|
|
||||||
dsymonds
|
|
||||||
cd1
|
|
||||||
wallclockbuilder
|
|
||||||
dansouza
|
|
27
vendor/github.com/google/uuid/LICENSE
generated
vendored
27
vendor/github.com/google/uuid/LICENSE
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
Copyright (c) 2009,2014 Google Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
19
vendor/github.com/google/uuid/README.md
generated
vendored
19
vendor/github.com/google/uuid/README.md
generated
vendored
|
@ -1,19 +0,0 @@
|
||||||
# uuid 
|
|
||||||
The uuid package generates and inspects UUIDs based on
|
|
||||||
[RFC 4122](http://tools.ietf.org/html/rfc4122)
|
|
||||||
and DCE 1.1: Authentication and Security Services.
|
|
||||||
|
|
||||||
This package is based on the github.com/pborman/uuid package (previously named
|
|
||||||
code.google.com/p/go-uuid). It differs from these earlier packages in that
|
|
||||||
a UUID is a 16 byte array rather than a byte slice. One loss due to this
|
|
||||||
change is the ability to represent an invalid UUID (vs a NIL UUID).
|
|
||||||
|
|
||||||
###### Install
|
|
||||||
`go get github.com/google/uuid`
|
|
||||||
|
|
||||||
###### Documentation
|
|
||||||
[](http://godoc.org/github.com/google/uuid)
|
|
||||||
|
|
||||||
Full `go doc` style documentation for the package can be viewed online without
|
|
||||||
installing this package by using the GoDoc site here:
|
|
||||||
http://pkg.go.dev/github.com/google/uuid
|
|
80
vendor/github.com/google/uuid/dce.go
generated
vendored
80
vendor/github.com/google/uuid/dce.go
generated
vendored
|
@ -1,80 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Domain represents a Version 2 domain
|
|
||||||
type Domain byte
|
|
||||||
|
|
||||||
// Domain constants for DCE Security (Version 2) UUIDs.
|
|
||||||
const (
|
|
||||||
Person = Domain(0)
|
|
||||||
Group = Domain(1)
|
|
||||||
Org = Domain(2)
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewDCESecurity returns a DCE Security (Version 2) UUID.
|
|
||||||
//
|
|
||||||
// The domain should be one of Person, Group or Org.
|
|
||||||
// On a POSIX system the id should be the users UID for the Person
|
|
||||||
// domain and the users GID for the Group. The meaning of id for
|
|
||||||
// the domain Org or on non-POSIX systems is site defined.
|
|
||||||
//
|
|
||||||
// For a given domain/id pair the same token may be returned for up to
|
|
||||||
// 7 minutes and 10 seconds.
|
|
||||||
func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
|
|
||||||
uuid, err := NewUUID()
|
|
||||||
if err == nil {
|
|
||||||
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
|
|
||||||
uuid[9] = byte(domain)
|
|
||||||
binary.BigEndian.PutUint32(uuid[0:], id)
|
|
||||||
}
|
|
||||||
return uuid, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
|
|
||||||
// domain with the id returned by os.Getuid.
|
|
||||||
//
|
|
||||||
// NewDCESecurity(Person, uint32(os.Getuid()))
|
|
||||||
func NewDCEPerson() (UUID, error) {
|
|
||||||
return NewDCESecurity(Person, uint32(os.Getuid()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
|
|
||||||
// domain with the id returned by os.Getgid.
|
|
||||||
//
|
|
||||||
// NewDCESecurity(Group, uint32(os.Getgid()))
|
|
||||||
func NewDCEGroup() (UUID, error) {
|
|
||||||
return NewDCESecurity(Group, uint32(os.Getgid()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Domain returns the domain for a Version 2 UUID. Domains are only defined
|
|
||||||
// for Version 2 UUIDs.
|
|
||||||
func (uuid UUID) Domain() Domain {
|
|
||||||
return Domain(uuid[9])
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
|
|
||||||
// UUIDs.
|
|
||||||
func (uuid UUID) ID() uint32 {
|
|
||||||
return binary.BigEndian.Uint32(uuid[0:4])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Domain) String() string {
|
|
||||||
switch d {
|
|
||||||
case Person:
|
|
||||||
return "Person"
|
|
||||||
case Group:
|
|
||||||
return "Group"
|
|
||||||
case Org:
|
|
||||||
return "Org"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("Domain%d", int(d))
|
|
||||||
}
|
|
12
vendor/github.com/google/uuid/doc.go
generated
vendored
12
vendor/github.com/google/uuid/doc.go
generated
vendored
|
@ -1,12 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package uuid generates and inspects UUIDs.
|
|
||||||
//
|
|
||||||
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security
|
|
||||||
// Services.
|
|
||||||
//
|
|
||||||
// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to
|
|
||||||
// maps or compared directly.
|
|
||||||
package uuid
|
|
53
vendor/github.com/google/uuid/hash.go
generated
vendored
53
vendor/github.com/google/uuid/hash.go
generated
vendored
|
@ -1,53 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"crypto/sha1"
|
|
||||||
"hash"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Well known namespace IDs and UUIDs
|
|
||||||
var (
|
|
||||||
NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
|
|
||||||
NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
|
|
||||||
NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
|
|
||||||
NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
|
|
||||||
Nil UUID // empty UUID, all zeros
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewHash returns a new UUID derived from the hash of space concatenated with
|
|
||||||
// data generated by h. The hash should be at least 16 byte in length. The
|
|
||||||
// first 16 bytes of the hash are used to form the UUID. The version of the
|
|
||||||
// UUID will be the lower 4 bits of version. NewHash is used to implement
|
|
||||||
// NewMD5 and NewSHA1.
|
|
||||||
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
|
|
||||||
h.Reset()
|
|
||||||
h.Write(space[:]) //nolint:errcheck
|
|
||||||
h.Write(data) //nolint:errcheck
|
|
||||||
s := h.Sum(nil)
|
|
||||||
var uuid UUID
|
|
||||||
copy(uuid[:], s)
|
|
||||||
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
|
|
||||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
|
|
||||||
return uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMD5 returns a new MD5 (Version 3) UUID based on the
|
|
||||||
// supplied name space and data. It is the same as calling:
|
|
||||||
//
|
|
||||||
// NewHash(md5.New(), space, data, 3)
|
|
||||||
func NewMD5(space UUID, data []byte) UUID {
|
|
||||||
return NewHash(md5.New(), space, data, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
|
|
||||||
// supplied name space and data. It is the same as calling:
|
|
||||||
//
|
|
||||||
// NewHash(sha1.New(), space, data, 5)
|
|
||||||
func NewSHA1(space UUID, data []byte) UUID {
|
|
||||||
return NewHash(sha1.New(), space, data, 5)
|
|
||||||
}
|
|
38
vendor/github.com/google/uuid/marshal.go
generated
vendored
38
vendor/github.com/google/uuid/marshal.go
generated
vendored
|
@ -1,38 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// MarshalText implements encoding.TextMarshaler.
|
|
||||||
func (uuid UUID) MarshalText() ([]byte, error) {
|
|
||||||
var js [36]byte
|
|
||||||
encodeHex(js[:], uuid)
|
|
||||||
return js[:], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
|
||||||
func (uuid *UUID) UnmarshalText(data []byte) error {
|
|
||||||
id, err := ParseBytes(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*uuid = id
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalBinary implements encoding.BinaryMarshaler.
|
|
||||||
func (uuid UUID) MarshalBinary() ([]byte, error) {
|
|
||||||
return uuid[:], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
|
|
||||||
func (uuid *UUID) UnmarshalBinary(data []byte) error {
|
|
||||||
if len(data) != 16 {
|
|
||||||
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
|
|
||||||
}
|
|
||||||
copy(uuid[:], data)
|
|
||||||
return nil
|
|
||||||
}
|
|
90
vendor/github.com/google/uuid/node.go
generated
vendored
90
vendor/github.com/google/uuid/node.go
generated
vendored
|
@ -1,90 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
nodeMu sync.Mutex
|
|
||||||
ifname string // name of interface being used
|
|
||||||
nodeID [6]byte // hardware for version 1 UUIDs
|
|
||||||
zeroID [6]byte // nodeID with only 0's
|
|
||||||
)
|
|
||||||
|
|
||||||
// NodeInterface returns the name of the interface from which the NodeID was
|
|
||||||
// derived. The interface "user" is returned if the NodeID was set by
|
|
||||||
// SetNodeID.
|
|
||||||
func NodeInterface() string {
|
|
||||||
defer nodeMu.Unlock()
|
|
||||||
nodeMu.Lock()
|
|
||||||
return ifname
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
|
|
||||||
// If name is "" then the first usable interface found will be used or a random
|
|
||||||
// Node ID will be generated. If a named interface cannot be found then false
|
|
||||||
// is returned.
|
|
||||||
//
|
|
||||||
// SetNodeInterface never fails when name is "".
|
|
||||||
func SetNodeInterface(name string) bool {
|
|
||||||
defer nodeMu.Unlock()
|
|
||||||
nodeMu.Lock()
|
|
||||||
return setNodeInterface(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setNodeInterface(name string) bool {
|
|
||||||
iname, addr := getHardwareInterface(name) // null implementation for js
|
|
||||||
if iname != "" && addr != nil {
|
|
||||||
ifname = iname
|
|
||||||
copy(nodeID[:], addr)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// We found no interfaces with a valid hardware address. If name
|
|
||||||
// does not specify a specific interface generate a random Node ID
|
|
||||||
// (section 4.1.6)
|
|
||||||
if name == "" {
|
|
||||||
ifname = "random"
|
|
||||||
randomBits(nodeID[:])
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
|
|
||||||
// if not already set.
|
|
||||||
func NodeID() []byte {
|
|
||||||
defer nodeMu.Unlock()
|
|
||||||
nodeMu.Lock()
|
|
||||||
if nodeID == zeroID {
|
|
||||||
setNodeInterface("")
|
|
||||||
}
|
|
||||||
nid := nodeID
|
|
||||||
return nid[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
|
|
||||||
// of id are used. If id is less than 6 bytes then false is returned and the
|
|
||||||
// Node ID is not set.
|
|
||||||
func SetNodeID(id []byte) bool {
|
|
||||||
if len(id) < 6 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer nodeMu.Unlock()
|
|
||||||
nodeMu.Lock()
|
|
||||||
copy(nodeID[:], id)
|
|
||||||
ifname = "user"
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
|
|
||||||
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
|
|
||||||
func (uuid UUID) NodeID() []byte {
|
|
||||||
var node [6]byte
|
|
||||||
copy(node[:], uuid[10:])
|
|
||||||
return node[:]
|
|
||||||
}
|
|
12
vendor/github.com/google/uuid/node_js.go
generated
vendored
12
vendor/github.com/google/uuid/node_js.go
generated
vendored
|
@ -1,12 +0,0 @@
|
||||||
// Copyright 2017 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build js
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
// getHardwareInterface returns nil values for the JS version of the code.
|
|
||||||
// This remvoves the "net" dependency, because it is not used in the browser.
|
|
||||||
// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
|
|
||||||
func getHardwareInterface(name string) (string, []byte) { return "", nil }
|
|
33
vendor/github.com/google/uuid/node_net.go
generated
vendored
33
vendor/github.com/google/uuid/node_net.go
generated
vendored
|
@ -1,33 +0,0 @@
|
||||||
// Copyright 2017 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build !js
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
var interfaces []net.Interface // cached list of interfaces
|
|
||||||
|
|
||||||
// getHardwareInterface returns the name and hardware address of interface name.
|
|
||||||
// If name is "" then the name and hardware address of one of the system's
|
|
||||||
// interfaces is returned. If no interfaces are found (name does not exist or
|
|
||||||
// there are no interfaces) then "", nil is returned.
|
|
||||||
//
|
|
||||||
// Only addresses of at least 6 bytes are returned.
|
|
||||||
func getHardwareInterface(name string) (string, []byte) {
|
|
||||||
if interfaces == nil {
|
|
||||||
var err error
|
|
||||||
interfaces, err = net.Interfaces()
|
|
||||||
if err != nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, ifs := range interfaces {
|
|
||||||
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
|
|
||||||
return ifs.Name, ifs.HardwareAddr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
118
vendor/github.com/google/uuid/null.go
generated
vendored
118
vendor/github.com/google/uuid/null.go
generated
vendored
|
@ -1,118 +0,0 @@
|
||||||
// Copyright 2021 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"database/sql/driver"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
var jsonNull = []byte("null")
|
|
||||||
|
|
||||||
// NullUUID represents a UUID that may be null.
|
|
||||||
// NullUUID implements the SQL driver.Scanner interface so
|
|
||||||
// it can be used as a scan destination:
|
|
||||||
//
|
|
||||||
// var u uuid.NullUUID
|
|
||||||
// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u)
|
|
||||||
// ...
|
|
||||||
// if u.Valid {
|
|
||||||
// // use u.UUID
|
|
||||||
// } else {
|
|
||||||
// // NULL value
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
type NullUUID struct {
|
|
||||||
UUID UUID
|
|
||||||
Valid bool // Valid is true if UUID is not NULL
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan implements the SQL driver.Scanner interface.
|
|
||||||
func (nu *NullUUID) Scan(value interface{}) error {
|
|
||||||
if value == nil {
|
|
||||||
nu.UUID, nu.Valid = Nil, false
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := nu.UUID.Scan(value)
|
|
||||||
if err != nil {
|
|
||||||
nu.Valid = false
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
nu.Valid = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value implements the driver Valuer interface.
|
|
||||||
func (nu NullUUID) Value() (driver.Value, error) {
|
|
||||||
if !nu.Valid {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
// Delegate to UUID Value function
|
|
||||||
return nu.UUID.Value()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalBinary implements encoding.BinaryMarshaler.
|
|
||||||
func (nu NullUUID) MarshalBinary() ([]byte, error) {
|
|
||||||
if nu.Valid {
|
|
||||||
return nu.UUID[:], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return []byte(nil), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
|
|
||||||
func (nu *NullUUID) UnmarshalBinary(data []byte) error {
|
|
||||||
if len(data) != 16 {
|
|
||||||
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
|
|
||||||
}
|
|
||||||
copy(nu.UUID[:], data)
|
|
||||||
nu.Valid = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalText implements encoding.TextMarshaler.
|
|
||||||
func (nu NullUUID) MarshalText() ([]byte, error) {
|
|
||||||
if nu.Valid {
|
|
||||||
return nu.UUID.MarshalText()
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonNull, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
|
||||||
func (nu *NullUUID) UnmarshalText(data []byte) error {
|
|
||||||
id, err := ParseBytes(data)
|
|
||||||
if err != nil {
|
|
||||||
nu.Valid = false
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
nu.UUID = id
|
|
||||||
nu.Valid = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler.
|
|
||||||
func (nu NullUUID) MarshalJSON() ([]byte, error) {
|
|
||||||
if nu.Valid {
|
|
||||||
return json.Marshal(nu.UUID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonNull, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler.
|
|
||||||
func (nu *NullUUID) UnmarshalJSON(data []byte) error {
|
|
||||||
if bytes.Equal(data, jsonNull) {
|
|
||||||
*nu = NullUUID{}
|
|
||||||
return nil // valid null UUID
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(data, &nu.UUID)
|
|
||||||
nu.Valid = err == nil
|
|
||||||
return err
|
|
||||||
}
|
|
59
vendor/github.com/google/uuid/sql.go
generated
vendored
59
vendor/github.com/google/uuid/sql.go
generated
vendored
|
@ -1,59 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql/driver"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Scan implements sql.Scanner so UUIDs can be read from databases transparently.
|
|
||||||
// Currently, database types that map to string and []byte are supported. Please
|
|
||||||
// consult database-specific driver documentation for matching types.
|
|
||||||
func (uuid *UUID) Scan(src interface{}) error {
|
|
||||||
switch src := src.(type) {
|
|
||||||
case nil:
|
|
||||||
return nil
|
|
||||||
|
|
||||||
case string:
|
|
||||||
// if an empty UUID comes from a table, we return a null UUID
|
|
||||||
if src == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// see Parse for required string format
|
|
||||||
u, err := Parse(src)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Scan: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
*uuid = u
|
|
||||||
|
|
||||||
case []byte:
|
|
||||||
// if an empty UUID comes from a table, we return a null UUID
|
|
||||||
if len(src) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// assumes a simple slice of bytes if 16 bytes
|
|
||||||
// otherwise attempts to parse
|
|
||||||
if len(src) != 16 {
|
|
||||||
return uuid.Scan(string(src))
|
|
||||||
}
|
|
||||||
copy((*uuid)[:], src)
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value implements sql.Valuer so that UUIDs can be written to databases
|
|
||||||
// transparently. Currently, UUIDs map to strings. Please consult
|
|
||||||
// database-specific driver documentation for matching types.
|
|
||||||
func (uuid UUID) Value() (driver.Value, error) {
|
|
||||||
return uuid.String(), nil
|
|
||||||
}
|
|
123
vendor/github.com/google/uuid/time.go
generated
vendored
123
vendor/github.com/google/uuid/time.go
generated
vendored
|
@ -1,123 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
|
|
||||||
// 1582.
|
|
||||||
type Time int64
|
|
||||||
|
|
||||||
const (
|
|
||||||
lillian = 2299160 // Julian day of 15 Oct 1582
|
|
||||||
unix = 2440587 // Julian day of 1 Jan 1970
|
|
||||||
epoch = unix - lillian // Days between epochs
|
|
||||||
g1582 = epoch * 86400 // seconds between epochs
|
|
||||||
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
timeMu sync.Mutex
|
|
||||||
lasttime uint64 // last time we returned
|
|
||||||
clockSeq uint16 // clock sequence for this run
|
|
||||||
|
|
||||||
timeNow = time.Now // for testing
|
|
||||||
)
|
|
||||||
|
|
||||||
// UnixTime converts t the number of seconds and nanoseconds using the Unix
|
|
||||||
// epoch of 1 Jan 1970.
|
|
||||||
func (t Time) UnixTime() (sec, nsec int64) {
|
|
||||||
sec = int64(t - g1582ns100)
|
|
||||||
nsec = (sec % 10000000) * 100
|
|
||||||
sec /= 10000000
|
|
||||||
return sec, nsec
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
|
|
||||||
// clock sequence as well as adjusting the clock sequence as needed. An error
|
|
||||||
// is returned if the current time cannot be determined.
|
|
||||||
func GetTime() (Time, uint16, error) {
|
|
||||||
defer timeMu.Unlock()
|
|
||||||
timeMu.Lock()
|
|
||||||
return getTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTime() (Time, uint16, error) {
|
|
||||||
t := timeNow()
|
|
||||||
|
|
||||||
// If we don't have a clock sequence already, set one.
|
|
||||||
if clockSeq == 0 {
|
|
||||||
setClockSequence(-1)
|
|
||||||
}
|
|
||||||
now := uint64(t.UnixNano()/100) + g1582ns100
|
|
||||||
|
|
||||||
// If time has gone backwards with this clock sequence then we
|
|
||||||
// increment the clock sequence
|
|
||||||
if now <= lasttime {
|
|
||||||
clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000
|
|
||||||
}
|
|
||||||
lasttime = now
|
|
||||||
return Time(now), clockSeq, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClockSequence returns the current clock sequence, generating one if not
|
|
||||||
// already set. The clock sequence is only used for Version 1 UUIDs.
|
|
||||||
//
|
|
||||||
// The uuid package does not use global static storage for the clock sequence or
|
|
||||||
// the last time a UUID was generated. Unless SetClockSequence is used, a new
|
|
||||||
// random clock sequence is generated the first time a clock sequence is
|
|
||||||
// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1)
|
|
||||||
func ClockSequence() int {
|
|
||||||
defer timeMu.Unlock()
|
|
||||||
timeMu.Lock()
|
|
||||||
return clockSequence()
|
|
||||||
}
|
|
||||||
|
|
||||||
func clockSequence() int {
|
|
||||||
if clockSeq == 0 {
|
|
||||||
setClockSequence(-1)
|
|
||||||
}
|
|
||||||
return int(clockSeq & 0x3fff)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to
|
|
||||||
// -1 causes a new sequence to be generated.
|
|
||||||
func SetClockSequence(seq int) {
|
|
||||||
defer timeMu.Unlock()
|
|
||||||
timeMu.Lock()
|
|
||||||
setClockSequence(seq)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setClockSequence(seq int) {
|
|
||||||
if seq == -1 {
|
|
||||||
var b [2]byte
|
|
||||||
randomBits(b[:]) // clock sequence
|
|
||||||
seq = int(b[0])<<8 | int(b[1])
|
|
||||||
}
|
|
||||||
oldSeq := clockSeq
|
|
||||||
clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant
|
|
||||||
if oldSeq != clockSeq {
|
|
||||||
lasttime = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
|
|
||||||
// uuid. The time is only defined for version 1 and 2 UUIDs.
|
|
||||||
func (uuid UUID) Time() Time {
|
|
||||||
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
|
||||||
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
|
||||||
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
|
||||||
return Time(time)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClockSequence returns the clock sequence encoded in uuid.
|
|
||||||
// The clock sequence is only well defined for version 1 and 2 UUIDs.
|
|
||||||
func (uuid UUID) ClockSequence() int {
|
|
||||||
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff
|
|
||||||
}
|
|
43
vendor/github.com/google/uuid/util.go
generated
vendored
43
vendor/github.com/google/uuid/util.go
generated
vendored
|
@ -1,43 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// randomBits completely fills slice b with random data.
|
|
||||||
func randomBits(b []byte) {
|
|
||||||
if _, err := io.ReadFull(rander, b); err != nil {
|
|
||||||
panic(err.Error()) // rand should never fail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// xvalues returns the value of a byte as a hexadecimal digit or 255.
|
|
||||||
var xvalues = [256]byte{
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
}
|
|
||||||
|
|
||||||
// xtob converts hex characters x1 and x2 into a byte.
|
|
||||||
func xtob(x1, x2 byte) (byte, bool) {
|
|
||||||
b1 := xvalues[x1]
|
|
||||||
b2 := xvalues[x2]
|
|
||||||
return (b1 << 4) | b2, b1 != 255 && b2 != 255
|
|
||||||
}
|
|
294
vendor/github.com/google/uuid/uuid.go
generated
vendored
294
vendor/github.com/google/uuid/uuid.go
generated
vendored
|
@ -1,294 +0,0 @@
|
||||||
// Copyright 2018 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
|
|
||||||
// 4122.
|
|
||||||
type UUID [16]byte
|
|
||||||
|
|
||||||
// A Version represents a UUID's version.
|
|
||||||
type Version byte
|
|
||||||
|
|
||||||
// A Variant represents a UUID's variant.
|
|
||||||
type Variant byte
|
|
||||||
|
|
||||||
// Constants returned by Variant.
|
|
||||||
const (
|
|
||||||
Invalid = Variant(iota) // Invalid UUID
|
|
||||||
RFC4122 // The variant specified in RFC4122
|
|
||||||
Reserved // Reserved, NCS backward compatibility.
|
|
||||||
Microsoft // Reserved, Microsoft Corporation backward compatibility.
|
|
||||||
Future // Reserved for future definition.
|
|
||||||
)
|
|
||||||
|
|
||||||
const randPoolSize = 16 * 16
|
|
||||||
|
|
||||||
var (
|
|
||||||
rander = rand.Reader // random function
|
|
||||||
poolEnabled = false
|
|
||||||
poolMu sync.Mutex
|
|
||||||
poolPos = randPoolSize // protected with poolMu
|
|
||||||
pool [randPoolSize]byte // protected with poolMu
|
|
||||||
)
|
|
||||||
|
|
||||||
type invalidLengthError struct{ len int }
|
|
||||||
|
|
||||||
func (err invalidLengthError) Error() string {
|
|
||||||
return fmt.Sprintf("invalid UUID length: %d", err.len)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsInvalidLengthError is matcher function for custom error invalidLengthError
|
|
||||||
func IsInvalidLengthError(err error) bool {
|
|
||||||
_, ok := err.(invalidLengthError)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse decodes s into a UUID or returns an error. Both the standard UUID
|
|
||||||
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
|
||||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
|
|
||||||
// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex
|
|
||||||
// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
|
|
||||||
func Parse(s string) (UUID, error) {
|
|
||||||
var uuid UUID
|
|
||||||
switch len(s) {
|
|
||||||
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
||||||
case 36:
|
|
||||||
|
|
||||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
||||||
case 36 + 9:
|
|
||||||
if strings.ToLower(s[:9]) != "urn:uuid:" {
|
|
||||||
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
|
|
||||||
}
|
|
||||||
s = s[9:]
|
|
||||||
|
|
||||||
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
|
|
||||||
case 36 + 2:
|
|
||||||
s = s[1:]
|
|
||||||
|
|
||||||
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
||||||
case 32:
|
|
||||||
var ok bool
|
|
||||||
for i := range uuid {
|
|
||||||
uuid[i], ok = xtob(s[i*2], s[i*2+1])
|
|
||||||
if !ok {
|
|
||||||
return uuid, errors.New("invalid UUID format")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return uuid, nil
|
|
||||||
default:
|
|
||||||
return uuid, invalidLengthError{len(s)}
|
|
||||||
}
|
|
||||||
// s is now at least 36 bytes long
|
|
||||||
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
||||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
|
||||||
return uuid, errors.New("invalid UUID format")
|
|
||||||
}
|
|
||||||
for i, x := range [16]int{
|
|
||||||
0, 2, 4, 6,
|
|
||||||
9, 11,
|
|
||||||
14, 16,
|
|
||||||
19, 21,
|
|
||||||
24, 26, 28, 30, 32, 34} {
|
|
||||||
v, ok := xtob(s[x], s[x+1])
|
|
||||||
if !ok {
|
|
||||||
return uuid, errors.New("invalid UUID format")
|
|
||||||
}
|
|
||||||
uuid[i] = v
|
|
||||||
}
|
|
||||||
return uuid, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseBytes is like Parse, except it parses a byte slice instead of a string.
|
|
||||||
func ParseBytes(b []byte) (UUID, error) {
|
|
||||||
var uuid UUID
|
|
||||||
switch len(b) {
|
|
||||||
case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
||||||
case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
||||||
if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) {
|
|
||||||
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
|
|
||||||
}
|
|
||||||
b = b[9:]
|
|
||||||
case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
|
|
||||||
b = b[1:]
|
|
||||||
case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
||||||
var ok bool
|
|
||||||
for i := 0; i < 32; i += 2 {
|
|
||||||
uuid[i/2], ok = xtob(b[i], b[i+1])
|
|
||||||
if !ok {
|
|
||||||
return uuid, errors.New("invalid UUID format")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return uuid, nil
|
|
||||||
default:
|
|
||||||
return uuid, invalidLengthError{len(b)}
|
|
||||||
}
|
|
||||||
// s is now at least 36 bytes long
|
|
||||||
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
||||||
if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
|
|
||||||
return uuid, errors.New("invalid UUID format")
|
|
||||||
}
|
|
||||||
for i, x := range [16]int{
|
|
||||||
0, 2, 4, 6,
|
|
||||||
9, 11,
|
|
||||||
14, 16,
|
|
||||||
19, 21,
|
|
||||||
24, 26, 28, 30, 32, 34} {
|
|
||||||
v, ok := xtob(b[x], b[x+1])
|
|
||||||
if !ok {
|
|
||||||
return uuid, errors.New("invalid UUID format")
|
|
||||||
}
|
|
||||||
uuid[i] = v
|
|
||||||
}
|
|
||||||
return uuid, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustParse is like Parse but panics if the string cannot be parsed.
|
|
||||||
// It simplifies safe initialization of global variables holding compiled UUIDs.
|
|
||||||
func MustParse(s string) UUID {
|
|
||||||
uuid, err := Parse(s)
|
|
||||||
if err != nil {
|
|
||||||
panic(`uuid: Parse(` + s + `): ` + err.Error())
|
|
||||||
}
|
|
||||||
return uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromBytes creates a new UUID from a byte slice. Returns an error if the slice
|
|
||||||
// does not have a length of 16. The bytes are copied from the slice.
|
|
||||||
func FromBytes(b []byte) (uuid UUID, err error) {
|
|
||||||
err = uuid.UnmarshalBinary(b)
|
|
||||||
return uuid, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must returns uuid if err is nil and panics otherwise.
|
|
||||||
func Must(uuid UUID, err error) UUID {
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
||||||
// , or "" if uuid is invalid.
|
|
||||||
func (uuid UUID) String() string {
|
|
||||||
var buf [36]byte
|
|
||||||
encodeHex(buf[:], uuid)
|
|
||||||
return string(buf[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// URN returns the RFC 2141 URN form of uuid,
|
|
||||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
|
|
||||||
func (uuid UUID) URN() string {
|
|
||||||
var buf [36 + 9]byte
|
|
||||||
copy(buf[:], "urn:uuid:")
|
|
||||||
encodeHex(buf[9:], uuid)
|
|
||||||
return string(buf[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeHex(dst []byte, uuid UUID) {
|
|
||||||
hex.Encode(dst, uuid[:4])
|
|
||||||
dst[8] = '-'
|
|
||||||
hex.Encode(dst[9:13], uuid[4:6])
|
|
||||||
dst[13] = '-'
|
|
||||||
hex.Encode(dst[14:18], uuid[6:8])
|
|
||||||
dst[18] = '-'
|
|
||||||
hex.Encode(dst[19:23], uuid[8:10])
|
|
||||||
dst[23] = '-'
|
|
||||||
hex.Encode(dst[24:], uuid[10:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variant returns the variant encoded in uuid.
|
|
||||||
func (uuid UUID) Variant() Variant {
|
|
||||||
switch {
|
|
||||||
case (uuid[8] & 0xc0) == 0x80:
|
|
||||||
return RFC4122
|
|
||||||
case (uuid[8] & 0xe0) == 0xc0:
|
|
||||||
return Microsoft
|
|
||||||
case (uuid[8] & 0xe0) == 0xe0:
|
|
||||||
return Future
|
|
||||||
default:
|
|
||||||
return Reserved
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version returns the version of uuid.
|
|
||||||
func (uuid UUID) Version() Version {
|
|
||||||
return Version(uuid[6] >> 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Version) String() string {
|
|
||||||
if v > 15 {
|
|
||||||
return fmt.Sprintf("BAD_VERSION_%d", v)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("VERSION_%d", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Variant) String() string {
|
|
||||||
switch v {
|
|
||||||
case RFC4122:
|
|
||||||
return "RFC4122"
|
|
||||||
case Reserved:
|
|
||||||
return "Reserved"
|
|
||||||
case Microsoft:
|
|
||||||
return "Microsoft"
|
|
||||||
case Future:
|
|
||||||
return "Future"
|
|
||||||
case Invalid:
|
|
||||||
return "Invalid"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("BadVariant%d", int(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRand sets the random number generator to r, which implements io.Reader.
|
|
||||||
// If r.Read returns an error when the package requests random data then
|
|
||||||
// a panic will be issued.
|
|
||||||
//
|
|
||||||
// Calling SetRand with nil sets the random number generator to the default
|
|
||||||
// generator.
|
|
||||||
func SetRand(r io.Reader) {
|
|
||||||
if r == nil {
|
|
||||||
rander = rand.Reader
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rander = r
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableRandPool enables internal randomness pool used for Random
|
|
||||||
// (Version 4) UUID generation. The pool contains random bytes read from
|
|
||||||
// the random number generator on demand in batches. Enabling the pool
|
|
||||||
// may improve the UUID generation throughput significantly.
|
|
||||||
//
|
|
||||||
// Since the pool is stored on the Go heap, this feature may be a bad fit
|
|
||||||
// for security sensitive applications.
|
|
||||||
//
|
|
||||||
// Both EnableRandPool and DisableRandPool are not thread-safe and should
|
|
||||||
// only be called when there is no possibility that New or any other
|
|
||||||
// UUID Version 4 generation function will be called concurrently.
|
|
||||||
func EnableRandPool() {
|
|
||||||
poolEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableRandPool disables the randomness pool if it was previously
|
|
||||||
// enabled with EnableRandPool.
|
|
||||||
//
|
|
||||||
// Both EnableRandPool and DisableRandPool are not thread-safe and should
|
|
||||||
// only be called when there is no possibility that New or any other
|
|
||||||
// UUID Version 4 generation function will be called concurrently.
|
|
||||||
func DisableRandPool() {
|
|
||||||
poolEnabled = false
|
|
||||||
defer poolMu.Unlock()
|
|
||||||
poolMu.Lock()
|
|
||||||
poolPos = randPoolSize
|
|
||||||
}
|
|
44
vendor/github.com/google/uuid/version1.go
generated
vendored
44
vendor/github.com/google/uuid/version1.go
generated
vendored
|
@ -1,44 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewUUID returns a Version 1 UUID based on the current NodeID and clock
|
|
||||||
// sequence, and the current time. If the NodeID has not been set by SetNodeID
|
|
||||||
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
|
|
||||||
// be set NewUUID returns nil. If clock sequence has not been set by
|
|
||||||
// SetClockSequence then it will be set automatically. If GetTime fails to
|
|
||||||
// return the current NewUUID returns nil and an error.
|
|
||||||
//
|
|
||||||
// In most cases, New should be used.
|
|
||||||
func NewUUID() (UUID, error) {
|
|
||||||
var uuid UUID
|
|
||||||
now, seq, err := GetTime()
|
|
||||||
if err != nil {
|
|
||||||
return uuid, err
|
|
||||||
}
|
|
||||||
|
|
||||||
timeLow := uint32(now & 0xffffffff)
|
|
||||||
timeMid := uint16((now >> 32) & 0xffff)
|
|
||||||
timeHi := uint16((now >> 48) & 0x0fff)
|
|
||||||
timeHi |= 0x1000 // Version 1
|
|
||||||
|
|
||||||
binary.BigEndian.PutUint32(uuid[0:], timeLow)
|
|
||||||
binary.BigEndian.PutUint16(uuid[4:], timeMid)
|
|
||||||
binary.BigEndian.PutUint16(uuid[6:], timeHi)
|
|
||||||
binary.BigEndian.PutUint16(uuid[8:], seq)
|
|
||||||
|
|
||||||
nodeMu.Lock()
|
|
||||||
if nodeID == zeroID {
|
|
||||||
setNodeInterface("")
|
|
||||||
}
|
|
||||||
copy(uuid[10:], nodeID[:])
|
|
||||||
nodeMu.Unlock()
|
|
||||||
|
|
||||||
return uuid, nil
|
|
||||||
}
|
|
76
vendor/github.com/google/uuid/version4.go
generated
vendored
76
vendor/github.com/google/uuid/version4.go
generated
vendored
|
@ -1,76 +0,0 @@
|
||||||
// Copyright 2016 Google Inc. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package uuid
|
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
// New creates a new random UUID or panics. New is equivalent to
|
|
||||||
// the expression
|
|
||||||
//
|
|
||||||
// uuid.Must(uuid.NewRandom())
|
|
||||||
func New() UUID {
|
|
||||||
return Must(NewRandom())
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewString creates a new random UUID and returns it as a string or panics.
|
|
||||||
// NewString is equivalent to the expression
|
|
||||||
//
|
|
||||||
// uuid.New().String()
|
|
||||||
func NewString() string {
|
|
||||||
return Must(NewRandom()).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRandom returns a Random (Version 4) UUID.
|
|
||||||
//
|
|
||||||
// The strength of the UUIDs is based on the strength of the crypto/rand
|
|
||||||
// package.
|
|
||||||
//
|
|
||||||
// Uses the randomness pool if it was enabled with EnableRandPool.
|
|
||||||
//
|
|
||||||
// A note about uniqueness derived from the UUID Wikipedia entry:
|
|
||||||
//
|
|
||||||
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
|
|
||||||
// hit by a meteorite is estimated to be one chance in 17 billion, that
|
|
||||||
// means the probability is about 0.00000000006 (6 × 10−11),
|
|
||||||
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
|
|
||||||
// year and having one duplicate.
|
|
||||||
func NewRandom() (UUID, error) {
|
|
||||||
if !poolEnabled {
|
|
||||||
return NewRandomFromReader(rander)
|
|
||||||
}
|
|
||||||
return newRandomFromPool()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader.
|
|
||||||
func NewRandomFromReader(r io.Reader) (UUID, error) {
|
|
||||||
var uuid UUID
|
|
||||||
_, err := io.ReadFull(r, uuid[:])
|
|
||||||
if err != nil {
|
|
||||||
return Nil, err
|
|
||||||
}
|
|
||||||
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
|
|
||||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
|
|
||||||
return uuid, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRandomFromPool() (UUID, error) {
|
|
||||||
var uuid UUID
|
|
||||||
poolMu.Lock()
|
|
||||||
if poolPos == randPoolSize {
|
|
||||||
_, err := io.ReadFull(rander, pool[:])
|
|
||||||
if err != nil {
|
|
||||||
poolMu.Unlock()
|
|
||||||
return Nil, err
|
|
||||||
}
|
|
||||||
poolPos = 0
|
|
||||||
}
|
|
||||||
copy(uuid[:], pool[poolPos:(poolPos+16)])
|
|
||||||
poolPos += 16
|
|
||||||
poolMu.Unlock()
|
|
||||||
|
|
||||||
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
|
|
||||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
|
|
||||||
return uuid, nil
|
|
||||||
}
|
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
|
@ -11,7 +11,6 @@ github.com/gofrs/uuid
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
# github.com/google/uuid v1.3.0
|
# github.com/google/uuid v1.3.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/google/uuid
|
|
||||||
# github.com/gorilla/handlers v1.5.1
|
# github.com/gorilla/handlers v1.5.1
|
||||||
## explicit; go 1.14
|
## explicit; go 1.14
|
||||||
github.com/gorilla/handlers
|
github.com/gorilla/handlers
|
||||||
|
|
Binary file not shown.
BIN
webapi
Executable file
BIN
webapi
Executable file
Binary file not shown.
27
webui/.gitignore
vendored
Normal file
27
webui/.gitignore
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
/dist
|
||||||
|
/dist-ssr
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
3
webui/.vscode/extensions.json
vendored
Normal file
3
webui/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||||
|
}
|
16
webui/index.html
Normal file
16
webui/index.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Example app</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/favicon.ico"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
|
||||||
|
<script src="/bootstrap/js/bootstrap.bundle.js"></script>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
1
webui/node_modules/.bin/esbuild
generated
vendored
Symbolic link
1
webui/node_modules/.bin/esbuild
generated
vendored
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../esbuild/bin/esbuild
|
1
webui/node_modules/.bin/nanoid
generated
vendored
Symbolic link
1
webui/node_modules/.bin/nanoid
generated
vendored
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../nanoid/bin/nanoid.cjs
|
1
webui/node_modules/.bin/parser
generated
vendored
Symbolic link
1
webui/node_modules/.bin/parser
generated
vendored
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../@babel/parser/bin/babel-parser.js
|
1
webui/node_modules/.bin/resolve
generated
vendored
Symbolic link
1
webui/node_modules/.bin/resolve
generated
vendored
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../resolve/bin/resolve
|
1
webui/node_modules/.bin/rollup
generated
vendored
Symbolic link
1
webui/node_modules/.bin/rollup
generated
vendored
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../rollup/dist/bin/rollup
|
1
webui/node_modules/.bin/vite
generated
vendored
Symbolic link
1
webui/node_modules/.bin/vite
generated
vendored
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../vite/bin/vite.js
|
500
webui/node_modules/.package-lock.json
generated
vendored
Normal file
500
webui/node_modules/.package-lock.json
generated
vendored
Normal file
|
@ -0,0 +1,500 @@
|
||||||
|
{
|
||||||
|
"name": "teapot",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"node_modules/@babel/parser": {
|
||||||
|
"version": "7.18.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz",
|
||||||
|
"integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==",
|
||||||
|
"bin": {
|
||||||
|
"parser": "bin/babel-parser.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-U4zNBlz9mg+TA+i+5QPc3N5lQvdUXENZLO2h0Wdzp56gI1MWhqJOv+6R+d4kOzoaSSq6TnGPBdZAXKOe4lXy6g==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vite": "^3.0.0",
|
||||||
|
"vue": "^3.2.25"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-core": {
|
||||||
|
"version": "3.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.37.tgz",
|
||||||
|
"integrity": "sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.16.4",
|
||||||
|
"@vue/shared": "3.2.37",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"source-map": "^0.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-dom": {
|
||||||
|
"version": "3.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz",
|
||||||
|
"integrity": "sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-core": "3.2.37",
|
||||||
|
"@vue/shared": "3.2.37"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-sfc": {
|
||||||
|
"version": "3.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz",
|
||||||
|
"integrity": "sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.16.4",
|
||||||
|
"@vue/compiler-core": "3.2.37",
|
||||||
|
"@vue/compiler-dom": "3.2.37",
|
||||||
|
"@vue/compiler-ssr": "3.2.37",
|
||||||
|
"@vue/reactivity-transform": "3.2.37",
|
||||||
|
"@vue/shared": "3.2.37",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"magic-string": "^0.25.7",
|
||||||
|
"postcss": "^8.1.10",
|
||||||
|
"source-map": "^0.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-ssr": {
|
||||||
|
"version": "3.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz",
|
||||||
|
"integrity": "sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-dom": "3.2.37",
|
||||||
|
"@vue/shared": "3.2.37"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/devtools-api": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@vue/reactivity": {
|
||||||
|
"version": "3.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz",
|
||||||
|
"integrity": "sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/shared": "3.2.37"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/reactivity-transform": {
|
||||||
|
"version": "3.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz",
|
||||||
|
"integrity": "sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.16.4",
|
||||||
|
"@vue/compiler-core": "3.2.37",
|
||||||
|
"@vue/shared": "3.2.37",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"magic-string": "^0.25.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/runtime-core": {
|
||||||
|
"version": "3.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.37.tgz",
|
||||||
|
"integrity": "sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/reactivity": "3.2.37",
|
||||||
|
"@vue/shared": "3.2.37"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/runtime-dom": {
|
||||||
|
"version": "3.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz",
|
||||||
|
"integrity": "sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/runtime-core": "3.2.37",
|
||||||
|
"@vue/shared": "3.2.37",
|
||||||
|
"csstype": "^2.6.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/server-renderer": {
|
||||||
|
"version": "3.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.37.tgz",
|
||||||
|
"integrity": "sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-ssr": "3.2.37",
|
||||||
|
"@vue/shared": "3.2.37"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "3.2.37"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/shared": {
|
||||||
|
"version": "3.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.37.tgz",
|
||||||
|
"integrity": "sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw=="
|
||||||
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "0.27.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
|
||||||
|
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.14.9",
|
||||||
|
"form-data": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/csstype": {
|
||||||
|
"version": "2.6.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
|
||||||
|
"integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
|
||||||
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/esbuild": {
|
||||||
|
"version": "0.14.54",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz",
|
||||||
|
"integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"bin": {
|
||||||
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@esbuild/linux-loong64": "0.14.54",
|
||||||
|
"esbuild-android-64": "0.14.54",
|
||||||
|
"esbuild-android-arm64": "0.14.54",
|
||||||
|
"esbuild-darwin-64": "0.14.54",
|
||||||
|
"esbuild-darwin-arm64": "0.14.54",
|
||||||
|
"esbuild-freebsd-64": "0.14.54",
|
||||||
|
"esbuild-freebsd-arm64": "0.14.54",
|
||||||
|
"esbuild-linux-32": "0.14.54",
|
||||||
|
"esbuild-linux-64": "0.14.54",
|
||||||
|
"esbuild-linux-arm": "0.14.54",
|
||||||
|
"esbuild-linux-arm64": "0.14.54",
|
||||||
|
"esbuild-linux-mips64le": "0.14.54",
|
||||||
|
"esbuild-linux-ppc64le": "0.14.54",
|
||||||
|
"esbuild-linux-riscv64": "0.14.54",
|
||||||
|
"esbuild-linux-s390x": "0.14.54",
|
||||||
|
"esbuild-netbsd-64": "0.14.54",
|
||||||
|
"esbuild-openbsd-64": "0.14.54",
|
||||||
|
"esbuild-sunos-64": "0.14.54",
|
||||||
|
"esbuild-windows-32": "0.14.54",
|
||||||
|
"esbuild-windows-64": "0.14.54",
|
||||||
|
"esbuild-windows-arm64": "0.14.54"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/esbuild-linux-64": {
|
||||||
|
"version": "0.14.54",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz",
|
||||||
|
"integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/estree-walker": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||||
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
|
||||||
|
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/function-bind": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/has": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"function-bind": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-core-module": {
|
||||||
|
"version": "2.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz",
|
||||||
|
"integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"has": "^1.0.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/magic-string": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"sourcemap-codec": "^1.4.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/nanoid": {
|
||||||
|
"version": "3.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||||
|
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-parse": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/picocolors": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
|
||||||
|
},
|
||||||
|
"node_modules/postcss": {
|
||||||
|
"version": "8.4.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz",
|
||||||
|
"integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^3.3.4",
|
||||||
|
"picocolors": "^1.0.0",
|
||||||
|
"source-map-js": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/resolve": {
|
||||||
|
"version": "1.22.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||||
|
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-core-module": "^2.9.0",
|
||||||
|
"path-parse": "^1.0.7",
|
||||||
|
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"resolve": "bin/resolve"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup": {
|
||||||
|
"version": "2.77.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.3.tgz",
|
||||||
|
"integrity": "sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"rollup": "dist/bin/rollup"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-js": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sourcemap-codec": {
|
||||||
|
"version": "1.4.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||||
|
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
|
||||||
|
},
|
||||||
|
"node_modules/supports-preserve-symlinks-flag": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite": {
|
||||||
|
"version": "3.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite/-/vite-3.0.9.tgz",
|
||||||
|
"integrity": "sha512-waYABTM+G6DBTCpYAxvevpG50UOlZuynR0ckTK5PawNVt7ebX6X7wNXHaGIO6wYYFXSM7/WcuFuO2QzhBB6aMw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"esbuild": "^0.14.47",
|
||||||
|
"postcss": "^8.4.16",
|
||||||
|
"resolve": "^1.22.1",
|
||||||
|
"rollup": ">=2.75.6 <2.77.0 || ~2.77.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"vite": "bin/vite.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"less": "*",
|
||||||
|
"sass": "*",
|
||||||
|
"stylus": "*",
|
||||||
|
"terser": "^5.4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"less": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"stylus": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"terser": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vue": {
|
||||||
|
"version": "3.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.37.tgz",
|
||||||
|
"integrity": "sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-dom": "3.2.37",
|
||||||
|
"@vue/compiler-sfc": "3.2.37",
|
||||||
|
"@vue/runtime-dom": "3.2.37",
|
||||||
|
"@vue/server-renderer": "3.2.37",
|
||||||
|
"@vue/shared": "3.2.37"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vue-router": {
|
||||||
|
"version": "4.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.4.tgz",
|
||||||
|
"integrity": "sha512-UgYen33gOtwT3cOG1+yRen+Brk9py8CSlC9LEa3UjvKZ4EAoSo8NjZPDeDnmNerfazorHIJG1NC7qdi1SuQJnQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.1.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/posva"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1073
webui/node_modules/@babel/parser/CHANGELOG.md
generated
vendored
Normal file
1073
webui/node_modules/@babel/parser/CHANGELOG.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
19
webui/node_modules/@babel/parser/LICENSE
generated
vendored
Normal file
19
webui/node_modules/@babel/parser/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (C) 2012-2014 by various contributors (see AUTHORS)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
19
webui/node_modules/@babel/parser/README.md
generated
vendored
Normal file
19
webui/node_modules/@babel/parser/README.md
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# @babel/parser
|
||||||
|
|
||||||
|
> A JavaScript parser
|
||||||
|
|
||||||
|
See our website [@babel/parser](https://babeljs.io/docs/en/babel-parser) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20parser%20(babylon)%22+is%3Aopen) associated with this package.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Using npm:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install --save-dev @babel/parser
|
||||||
|
```
|
||||||
|
|
||||||
|
or using yarn:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @babel/parser --dev
|
||||||
|
```
|
15
webui/node_modules/@babel/parser/bin/babel-parser.js
generated
vendored
Executable file
15
webui/node_modules/@babel/parser/bin/babel-parser.js
generated
vendored
Executable file
|
@ -0,0 +1,15 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
/* eslint no-var: 0 */
|
||||||
|
|
||||||
|
var parser = require("..");
|
||||||
|
var fs = require("fs");
|
||||||
|
|
||||||
|
var filename = process.argv[2];
|
||||||
|
if (!filename) {
|
||||||
|
console.error("no filename specified");
|
||||||
|
} else {
|
||||||
|
var file = fs.readFileSync(filename, "utf8");
|
||||||
|
var ast = parser.parse(file);
|
||||||
|
|
||||||
|
console.log(JSON.stringify(ast, null, " "));
|
||||||
|
}
|
5
webui/node_modules/@babel/parser/index.cjs
generated
vendored
Normal file
5
webui/node_modules/@babel/parser/index.cjs
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
try {
|
||||||
|
module.exports = require("./lib/index.cjs");
|
||||||
|
} catch {
|
||||||
|
module.exports = require("./lib/index.js");
|
||||||
|
}
|
16800
webui/node_modules/@babel/parser/lib/index.js
generated
vendored
Normal file
16800
webui/node_modules/@babel/parser/lib/index.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
webui/node_modules/@babel/parser/lib/index.js.map
generated
vendored
Normal file
1
webui/node_modules/@babel/parser/lib/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
46
webui/node_modules/@babel/parser/package.json
generated
vendored
Normal file
46
webui/node_modules/@babel/parser/package.json
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"name": "@babel/parser",
|
||||||
|
"version": "7.18.13",
|
||||||
|
"description": "A JavaScript parser",
|
||||||
|
"author": "The Babel Team (https://babel.dev/team)",
|
||||||
|
"homepage": "https://babel.dev/docs/en/next/babel-parser",
|
||||||
|
"bugs": "https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A+parser+%28babylon%29%22+is%3Aopen",
|
||||||
|
"license": "MIT",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"babel",
|
||||||
|
"javascript",
|
||||||
|
"parser",
|
||||||
|
"tc39",
|
||||||
|
"ecmascript",
|
||||||
|
"@babel/parser"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/babel/babel.git",
|
||||||
|
"directory": "packages/babel-parser"
|
||||||
|
},
|
||||||
|
"main": "./lib/index.js",
|
||||||
|
"types": "./typings/babel-parser.d.ts",
|
||||||
|
"files": [
|
||||||
|
"bin",
|
||||||
|
"lib",
|
||||||
|
"typings",
|
||||||
|
"index.cjs"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/code-frame": "^7.18.6",
|
||||||
|
"@babel/helper-check-duplicate-nodes": "^7.18.6",
|
||||||
|
"@babel/helper-fixtures": "^7.18.6",
|
||||||
|
"@babel/helper-string-parser": "^7.18.10",
|
||||||
|
"@babel/helper-validator-identifier": "^7.18.6",
|
||||||
|
"charcodes": "^0.2.0"
|
||||||
|
},
|
||||||
|
"bin": "./bin/babel-parser.js",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
214
webui/node_modules/@babel/parser/typings/babel-parser.d.ts
generated
vendored
Normal file
214
webui/node_modules/@babel/parser/typings/babel-parser.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
// Type definitions for @babel/parser
|
||||||
|
// Project: https://github.com/babel/babel/tree/main/packages/babel-parser
|
||||||
|
// Definitions by: Troy Gerwien <https://github.com/yortus>
|
||||||
|
// Marvin Hagemeister <https://github.com/marvinhagemeister>
|
||||||
|
// Avi Vahl <https://github.com/AviVahl>
|
||||||
|
// TypeScript Version: 2.9
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the provided code as an entire ECMAScript program.
|
||||||
|
*/
|
||||||
|
export function parse(
|
||||||
|
input: string,
|
||||||
|
options?: ParserOptions
|
||||||
|
): ParseResult<import("@babel/types").File>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the provided code as a single expression.
|
||||||
|
*/
|
||||||
|
export function parseExpression(
|
||||||
|
input: string,
|
||||||
|
options?: ParserOptions
|
||||||
|
): ParseResult<import("@babel/types").Expression>;
|
||||||
|
|
||||||
|
export interface ParserOptions {
|
||||||
|
/**
|
||||||
|
* By default, import and export declarations can only appear at a program's top level.
|
||||||
|
* Setting this option to true allows them anywhere where a statement is allowed.
|
||||||
|
*/
|
||||||
|
allowImportExportEverywhere?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, await use is not allowed outside of an async function.
|
||||||
|
* Set this to true to accept such code.
|
||||||
|
*/
|
||||||
|
allowAwaitOutsideFunction?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, a return statement at the top level raises an error.
|
||||||
|
* Set this to true to accept such code.
|
||||||
|
*/
|
||||||
|
allowReturnOutsideFunction?: boolean;
|
||||||
|
|
||||||
|
allowSuperOutsideMethod?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, exported identifiers must refer to a declared variable.
|
||||||
|
* Set this to true to allow export statements to reference undeclared variables.
|
||||||
|
*/
|
||||||
|
allowUndeclaredExports?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, Babel attaches comments to adjacent AST nodes.
|
||||||
|
* When this option is set to false, comments are not attached.
|
||||||
|
* It can provide up to 30% performance improvement when the input code has many comments.
|
||||||
|
* @babel/eslint-parser will set it for you.
|
||||||
|
* It is not recommended to use attachComment: false with Babel transform,
|
||||||
|
* as doing so removes all the comments in output code, and renders annotations such as
|
||||||
|
* /* istanbul ignore next *\/ nonfunctional.
|
||||||
|
*/
|
||||||
|
attachComment?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, Babel always throws an error when it finds some invalid code.
|
||||||
|
* When this option is set to true, it will store the parsing error and
|
||||||
|
* try to continue parsing the invalid input file.
|
||||||
|
*/
|
||||||
|
errorRecovery?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate the mode the code should be parsed in.
|
||||||
|
* Can be one of "script", "module", or "unambiguous". Defaults to "script".
|
||||||
|
* "unambiguous" will make @babel/parser attempt to guess, based on the presence
|
||||||
|
* of ES6 import or export statements.
|
||||||
|
* Files with ES6 imports and exports are considered "module" and are otherwise "script".
|
||||||
|
*/
|
||||||
|
sourceType?: "script" | "module" | "unambiguous";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correlate output AST nodes with their source filename.
|
||||||
|
* Useful when generating code and source maps from the ASTs of multiple input files.
|
||||||
|
*/
|
||||||
|
sourceFilename?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, the first line of code parsed is treated as line 1.
|
||||||
|
* You can provide a line number to alternatively start with.
|
||||||
|
* Useful for integration with other source tools.
|
||||||
|
*/
|
||||||
|
startLine?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, the parsed code is treated as if it starts from line 1, column 0.
|
||||||
|
* You can provide a column number to alternatively start with.
|
||||||
|
* Useful for integration with other source tools.
|
||||||
|
*/
|
||||||
|
startColumn?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array containing the plugins that you want to enable.
|
||||||
|
*/
|
||||||
|
plugins?: ParserPlugin[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the parser work in strict mode.
|
||||||
|
* Defaults to true if sourceType === 'module'. Otherwise, false.
|
||||||
|
*/
|
||||||
|
strictMode?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a ranges property to each node: [node.start, node.end]
|
||||||
|
*/
|
||||||
|
ranges?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all parsed tokens to a tokens property on the File node.
|
||||||
|
*/
|
||||||
|
tokens?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, the parser adds information about parentheses by setting
|
||||||
|
* `extra.parenthesized` to `true` as needed.
|
||||||
|
* When this option is `true` the parser creates `ParenthesizedExpression`
|
||||||
|
* AST nodes instead of using the `extra` property.
|
||||||
|
*/
|
||||||
|
createParenthesizedExpressions?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ParserPlugin =
|
||||||
|
| "asyncDoExpressions"
|
||||||
|
| "asyncGenerators"
|
||||||
|
| "bigInt"
|
||||||
|
| "classPrivateMethods"
|
||||||
|
| "classPrivateProperties"
|
||||||
|
| "classProperties"
|
||||||
|
| "classStaticBlock" // Enabled by default
|
||||||
|
| "decimal"
|
||||||
|
| "decorators"
|
||||||
|
| "decorators-legacy"
|
||||||
|
| "decoratorAutoAccessors"
|
||||||
|
| "destructuringPrivate"
|
||||||
|
| "doExpressions"
|
||||||
|
| "dynamicImport"
|
||||||
|
| "estree"
|
||||||
|
| "exportDefaultFrom"
|
||||||
|
| "exportNamespaceFrom" // deprecated
|
||||||
|
| "flow"
|
||||||
|
| "flowComments"
|
||||||
|
| "functionBind"
|
||||||
|
| "functionSent"
|
||||||
|
| "importMeta"
|
||||||
|
| "jsx"
|
||||||
|
| "logicalAssignment"
|
||||||
|
| "importAssertions"
|
||||||
|
| "moduleBlocks"
|
||||||
|
| "moduleStringNames"
|
||||||
|
| "nullishCoalescingOperator"
|
||||||
|
| "numericSeparator"
|
||||||
|
| "objectRestSpread"
|
||||||
|
| "optionalCatchBinding"
|
||||||
|
| "optionalChaining"
|
||||||
|
| "partialApplication"
|
||||||
|
| "pipelineOperator"
|
||||||
|
| "placeholders"
|
||||||
|
| "privateIn" // Enabled by default
|
||||||
|
| "regexpUnicodeSets"
|
||||||
|
| "throwExpressions"
|
||||||
|
| "topLevelAwait"
|
||||||
|
| "typescript"
|
||||||
|
| "v8intrinsic"
|
||||||
|
| ParserPluginWithOptions;
|
||||||
|
|
||||||
|
export type ParserPluginWithOptions =
|
||||||
|
| ["decorators", DecoratorsPluginOptions]
|
||||||
|
| ["pipelineOperator", PipelineOperatorPluginOptions]
|
||||||
|
| ["recordAndTuple", RecordAndTuplePluginOptions]
|
||||||
|
| ["flow", FlowPluginOptions]
|
||||||
|
| ["typescript", TypeScriptPluginOptions];
|
||||||
|
|
||||||
|
export interface DecoratorsPluginOptions {
|
||||||
|
decoratorsBeforeExport?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PipelineOperatorPluginOptions {
|
||||||
|
proposal: "minimal" | "fsharp" | "hack" | "smart";
|
||||||
|
topicToken?: "%" | "#" | "@@" | "^^" | "^";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RecordAndTuplePluginOptions {
|
||||||
|
syntaxType: "bar" | "hash";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlowPluginOptions {
|
||||||
|
all?: boolean;
|
||||||
|
enums?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TypeScriptPluginOptions {
|
||||||
|
dts?: boolean;
|
||||||
|
disallowAmbiguousJSXLike?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tokTypes: {
|
||||||
|
// todo(flow->ts) real token type
|
||||||
|
[name: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ParseError {
|
||||||
|
code: string;
|
||||||
|
reasonCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParseResult<Result> = Result & {
|
||||||
|
errors: ParseError[];
|
||||||
|
};
|
21
webui/node_modules/@vitejs/plugin-vue/LICENSE
generated
vendored
Normal file
21
webui/node_modules/@vitejs/plugin-vue/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019-present, Yuxi (Evan) You and Vite contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
171
webui/node_modules/@vitejs/plugin-vue/README.md
generated
vendored
Normal file
171
webui/node_modules/@vitejs/plugin-vue/README.md
generated
vendored
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
# @vitejs/plugin-vue [](https://npmjs.com/package/@vitejs/plugin-vue)
|
||||||
|
|
||||||
|
> Note: as of `vue` 3.2.13+ and `@vitejs/plugin-vue` 1.9.0+, `@vue/compiler-sfc` is no longer required as a peer dependency.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// vite.config.js
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: [vue()]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface Options {
|
||||||
|
include?: string | RegExp | (string | RegExp)[]
|
||||||
|
exclude?: string | RegExp | (string | RegExp)[]
|
||||||
|
|
||||||
|
ssr?: boolean
|
||||||
|
isProduction?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform Vue SFCs into custom elements (requires vue@^3.2.0)
|
||||||
|
* - `true` -> all `*.vue` imports are converted into custom elements
|
||||||
|
* - `string | RegExp` -> matched files are converted into custom elements
|
||||||
|
*
|
||||||
|
* @default /\.ce\.vue$/
|
||||||
|
*/
|
||||||
|
customElement?: boolean | string | RegExp | (string | RegExp)[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable Vue reactivity transform (experimental, requires vue@^3.2.25).
|
||||||
|
* https://github.com/vuejs/core/tree/master/packages/reactivity-transform
|
||||||
|
*
|
||||||
|
* - `true`: transform will be enabled for all vue,js(x),ts(x) files except
|
||||||
|
* those inside node_modules
|
||||||
|
* - `string | RegExp`: apply to vue + only matched files (will include
|
||||||
|
* node_modules, so specify directories in necessary)
|
||||||
|
* - `false`: disable in all cases
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
reactivityTransform?: boolean | string | RegExp | (string | RegExp)[]
|
||||||
|
|
||||||
|
// options to pass on to vue/compiler-sfc
|
||||||
|
script?: Partial<Pick<SFCScriptCompileOptions, 'babelParserPlugins'>>
|
||||||
|
template?: Partial<
|
||||||
|
Pick<
|
||||||
|
SFCTemplateCompileOptions,
|
||||||
|
| 'compiler'
|
||||||
|
| 'compilerOptions'
|
||||||
|
| 'preprocessOptions'
|
||||||
|
| 'preprocessCustomRequire'
|
||||||
|
| 'transformAssetUrls'
|
||||||
|
>
|
||||||
|
>
|
||||||
|
style?: Partial<Pick<SFCStyleCompileOptions, 'trim'>>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Asset URL handling
|
||||||
|
|
||||||
|
When `@vitejs/plugin-vue` compiles the `<template>` blocks in SFCs, it also converts any encountered asset URLs into ESM imports.
|
||||||
|
|
||||||
|
For example, the following template snippet:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<img src="../image.png" />
|
||||||
|
```
|
||||||
|
|
||||||
|
Is the same as:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
import _imports_0 from '../image.png'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<img src="_imports_0" />
|
||||||
|
```
|
||||||
|
|
||||||
|
By default the following tag/attribute combinations are transformed, and can be configured using the `template.transformAssetUrls` option.
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
video: ['src', 'poster'],
|
||||||
|
source: ['src'],
|
||||||
|
img: ['src'],
|
||||||
|
image: ['xlink:href', 'href'],
|
||||||
|
use: ['xlink:href', 'href']
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that only attribute values that are static strings are transformed. Otherwise, you'd need to import the asset manually, e.g. `import imgUrl from '../image.png'`.
|
||||||
|
|
||||||
|
## Example for passing options to `vue/compiler-sfc`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: [
|
||||||
|
vue({
|
||||||
|
template: {
|
||||||
|
compilerOptions: {
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
transformAssetUrls: {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example for transforming custom blocks
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import yaml from 'js-yaml'
|
||||||
|
|
||||||
|
const vueI18nPlugin = {
|
||||||
|
name: 'vue-i18n',
|
||||||
|
transform(code, id) {
|
||||||
|
if (!/vue&type=i18n/.test(id)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (/\.ya?ml$/.test(id)) {
|
||||||
|
code = JSON.stringify(yaml.load(code.trim()))
|
||||||
|
}
|
||||||
|
return `export default Comp => {
|
||||||
|
Comp.i18n = ${code}
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: [vue(), vueI18nPlugin]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using Vue SFCs as Custom Elements
|
||||||
|
|
||||||
|
> Requires `vue@^3.2.0` & `@vitejs/plugin-vue@^1.4.0`
|
||||||
|
|
||||||
|
Vue 3.2 introduces the `defineCustomElement` method, which works with SFCs. By default, `<style>` tags inside SFCs are extracted and merged into CSS files during build. However when shipping a library of custom elements, it may be desirable to inline the styles as JavaScript strings and inject them into the custom elements' shadow root instead.
|
||||||
|
|
||||||
|
Starting in 1.4.0, files ending with `*.ce.vue` will be compiled in "custom elements" mode: its `<style>` tags are compiled into inlined CSS strings and attached to the component as its `styles` property:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { defineCustomElement } from 'vue'
|
||||||
|
import Example from './Example.ce.vue'
|
||||||
|
|
||||||
|
console.log(Example.styles) // ['/* css content */']
|
||||||
|
|
||||||
|
// register
|
||||||
|
customElements.define('my-example', defineCustomElement(Example))
|
||||||
|
```
|
||||||
|
|
||||||
|
Note in custom elements mode there is no need to use `<style scoped>` since the CSS is already scoped inside the shadow DOM.
|
||||||
|
|
||||||
|
The `customElement` plugin option can be used to configure the behavior:
|
||||||
|
|
||||||
|
- `{ customElement: true }` will import all `*.vue` files in custom element mode.
|
||||||
|
- Use a string or regex pattern to change how files should be loaded as Custom Elements (this check is applied after `include` and `exclude` matches).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
2682
webui/node_modules/@vitejs/plugin-vue/dist/index.cjs
generated
vendored
Normal file
2682
webui/node_modules/@vitejs/plugin-vue/dist/index.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
61
webui/node_modules/@vitejs/plugin-vue/dist/index.d.ts
generated
vendored
Normal file
61
webui/node_modules/@vitejs/plugin-vue/dist/index.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import { ViteDevServer, Plugin } from 'vite';
|
||||||
|
import * as _compiler from 'vue/compiler-sfc';
|
||||||
|
import { SFCScriptCompileOptions, SFCTemplateCompileOptions, SFCStyleCompileOptions } from 'vue/compiler-sfc';
|
||||||
|
|
||||||
|
interface VueQuery {
|
||||||
|
vue?: boolean;
|
||||||
|
src?: string;
|
||||||
|
type?: 'script' | 'template' | 'style' | 'custom';
|
||||||
|
index?: number;
|
||||||
|
lang?: string;
|
||||||
|
raw?: boolean;
|
||||||
|
scoped?: boolean;
|
||||||
|
}
|
||||||
|
declare function parseVueRequest(id: string): {
|
||||||
|
filename: string;
|
||||||
|
query: VueQuery;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Options {
|
||||||
|
include?: string | RegExp | (string | RegExp)[];
|
||||||
|
exclude?: string | RegExp | (string | RegExp)[];
|
||||||
|
isProduction?: boolean;
|
||||||
|
script?: Partial<Pick<SFCScriptCompileOptions, 'babelParserPlugins'>>;
|
||||||
|
template?: Partial<Pick<SFCTemplateCompileOptions, 'compiler' | 'compilerOptions' | 'preprocessOptions' | 'preprocessCustomRequire' | 'transformAssetUrls'>>;
|
||||||
|
style?: Partial<Pick<SFCStyleCompileOptions, 'trim'>>;
|
||||||
|
/**
|
||||||
|
* Transform Vue SFCs into custom elements.
|
||||||
|
* - `true`: all `*.vue` imports are converted into custom elements
|
||||||
|
* - `string | RegExp`: matched files are converted into custom elements
|
||||||
|
*
|
||||||
|
* @default /\.ce\.vue$/
|
||||||
|
*/
|
||||||
|
customElement?: boolean | string | RegExp | (string | RegExp)[];
|
||||||
|
/**
|
||||||
|
* Enable Vue reactivity transform (experimental).
|
||||||
|
* https://github.com/vuejs/core/tree/master/packages/reactivity-transform
|
||||||
|
* - `true`: transform will be enabled for all vue,js(x),ts(x) files except
|
||||||
|
* those inside node_modules
|
||||||
|
* - `string | RegExp`: apply to vue + only matched files (will include
|
||||||
|
* node_modules, so specify directories in necessary)
|
||||||
|
* - `false`: disable in all cases
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
reactivityTransform?: boolean | string | RegExp | (string | RegExp)[];
|
||||||
|
/**
|
||||||
|
* Use custom compiler-sfc instance. Can be used to force a specific version.
|
||||||
|
*/
|
||||||
|
compiler?: typeof _compiler;
|
||||||
|
}
|
||||||
|
interface ResolvedOptions extends Options {
|
||||||
|
compiler: typeof _compiler;
|
||||||
|
root: string;
|
||||||
|
sourceMap: boolean;
|
||||||
|
cssDevSourcemap: boolean;
|
||||||
|
devServer?: ViteDevServer;
|
||||||
|
devToolsEnabled?: boolean;
|
||||||
|
}
|
||||||
|
declare function vuePlugin(rawOptions?: Options): Plugin;
|
||||||
|
|
||||||
|
export { Options, ResolvedOptions, VueQuery, vuePlugin as default, parseVueRequest };
|
2669
webui/node_modules/@vitejs/plugin-vue/dist/index.mjs
generated
vendored
Normal file
2669
webui/node_modules/@vitejs/plugin-vue/dist/index.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
51
webui/node_modules/@vitejs/plugin-vue/package.json
generated
vendored
Normal file
51
webui/node_modules/@vitejs/plugin-vue/package.json
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
"name": "@vitejs/plugin-vue",
|
||||||
|
"version": "3.0.3",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Evan You",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"main": "./dist/index.cjs",
|
||||||
|
"module": "./dist/index.mjs",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"import": "./dist/index.mjs",
|
||||||
|
"require": "./dist/index.cjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "unbuild --stub",
|
||||||
|
"build": "unbuild && pnpm run patch-cjs",
|
||||||
|
"patch-cjs": "tsx ../../scripts/patchCJS.ts",
|
||||||
|
"prepublishOnly": "npm run build"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/vitejs/vite.git",
|
||||||
|
"directory": "packages/plugin-vue"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/vitejs/vite/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/vitejs/vite/tree/main/packages/plugin-vue#readme",
|
||||||
|
"peerDependencies": {
|
||||||
|
"vite": "^3.0.0",
|
||||||
|
"vue": "^3.2.25"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@jridgewell/gen-mapping": "^0.3.2",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.14",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"rollup": ">=2.75.6 <2.77.0 || ~2.77.0",
|
||||||
|
"slash": "^4.0.0",
|
||||||
|
"source-map": "^0.6.1",
|
||||||
|
"vite": "workspace:*",
|
||||||
|
"vue": "^3.2.37"
|
||||||
|
}
|
||||||
|
}
|
21
webui/node_modules/@vue/compiler-core/LICENSE
generated
vendored
Normal file
21
webui/node_modules/@vue/compiler-core/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018-present, Yuxi (Evan) You
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
1
webui/node_modules/@vue/compiler-core/README.md
generated
vendored
Normal file
1
webui/node_modules/@vue/compiler-core/README.md
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# @vue/compiler-core
|
5808
webui/node_modules/@vue/compiler-core/dist/compiler-core.cjs.js
generated
vendored
Normal file
5808
webui/node_modules/@vue/compiler-core/dist/compiler-core.cjs.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
5678
webui/node_modules/@vue/compiler-core/dist/compiler-core.cjs.prod.js
generated
vendored
Normal file
5678
webui/node_modules/@vue/compiler-core/dist/compiler-core.cjs.prod.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1198
webui/node_modules/@vue/compiler-core/dist/compiler-core.d.ts
generated
vendored
Normal file
1198
webui/node_modules/@vue/compiler-core/dist/compiler-core.d.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
4861
webui/node_modules/@vue/compiler-core/dist/compiler-core.esm-bundler.js
generated
vendored
Normal file
4861
webui/node_modules/@vue/compiler-core/dist/compiler-core.esm-bundler.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
7
webui/node_modules/@vue/compiler-core/index.js
generated
vendored
Normal file
7
webui/node_modules/@vue/compiler-core/index.js
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
module.exports = require('./dist/compiler-core.cjs.prod.js')
|
||||||
|
} else {
|
||||||
|
module.exports = require('./dist/compiler-core.cjs.js')
|
||||||
|
}
|
43
webui/node_modules/@vue/compiler-core/package.json
generated
vendored
Normal file
43
webui/node_modules/@vue/compiler-core/package.json
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"name": "@vue/compiler-core",
|
||||||
|
"version": "3.2.37",
|
||||||
|
"description": "@vue/compiler-core",
|
||||||
|
"main": "index.js",
|
||||||
|
"module": "dist/compiler-core.esm-bundler.js",
|
||||||
|
"types": "dist/compiler-core.d.ts",
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"buildOptions": {
|
||||||
|
"name": "VueCompilerCore",
|
||||||
|
"compat": true,
|
||||||
|
"formats": [
|
||||||
|
"esm-bundler",
|
||||||
|
"cjs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/vuejs/core.git",
|
||||||
|
"directory": "packages/compiler-core"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"vue"
|
||||||
|
],
|
||||||
|
"author": "Evan You",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/vuejs/core/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-core#readme",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/shared": "3.2.37",
|
||||||
|
"@babel/parser": "^7.16.4",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"source-map": "^0.6.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/types": "^7.16.0"
|
||||||
|
}
|
||||||
|
}
|
21
webui/node_modules/@vue/compiler-dom/LICENSE
generated
vendored
Normal file
21
webui/node_modules/@vue/compiler-dom/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018-present, Yuxi (Evan) You
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
1
webui/node_modules/@vue/compiler-dom/README.md
generated
vendored
Normal file
1
webui/node_modules/@vue/compiler-dom/README.md
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# @vue/compiler-dom
|
3136
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.cjs.js
generated
vendored
Normal file
3136
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.cjs.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
3074
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.cjs.prod.js
generated
vendored
Normal file
3074
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.cjs.prod.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
66
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.d.ts
generated
vendored
Normal file
66
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import { CodegenResult } from '@vue/compiler-core';
|
||||||
|
import { CompilerError } from '@vue/compiler-core';
|
||||||
|
import { CompilerOptions } from '@vue/compiler-core';
|
||||||
|
import { DirectiveTransform } from '@vue/compiler-core';
|
||||||
|
import { NodeTransform } from '@vue/compiler-core';
|
||||||
|
import { ParserOptions } from '@vue/compiler-core';
|
||||||
|
import { RootNode } from '@vue/compiler-core';
|
||||||
|
import { SourceLocation } from '@vue/compiler-core';
|
||||||
|
|
||||||
|
export declare function compile(template: string, options?: CompilerOptions): CodegenResult;
|
||||||
|
|
||||||
|
export declare function createDOMCompilerError(code: DOMErrorCodes, loc?: SourceLocation): DOMCompilerError;
|
||||||
|
|
||||||
|
declare interface DOMCompilerError extends CompilerError {
|
||||||
|
code: DOMErrorCodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare const DOMDirectiveTransforms: Record<string, DirectiveTransform>;
|
||||||
|
|
||||||
|
export declare const enum DOMErrorCodes {
|
||||||
|
X_V_HTML_NO_EXPRESSION = 50,
|
||||||
|
X_V_HTML_WITH_CHILDREN = 51,
|
||||||
|
X_V_TEXT_NO_EXPRESSION = 52,
|
||||||
|
X_V_TEXT_WITH_CHILDREN = 53,
|
||||||
|
X_V_MODEL_ON_INVALID_ELEMENT = 54,
|
||||||
|
X_V_MODEL_ARG_ON_ELEMENT = 55,
|
||||||
|
X_V_MODEL_ON_FILE_INPUT_ELEMENT = 56,
|
||||||
|
X_V_MODEL_UNNECESSARY_VALUE = 57,
|
||||||
|
X_V_SHOW_NO_EXPRESSION = 58,
|
||||||
|
X_TRANSITION_INVALID_CHILDREN = 59,
|
||||||
|
X_IGNORED_SIDE_EFFECT_TAG = 60,
|
||||||
|
__EXTEND_POINT__ = 61
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare const DOMNodeTransforms: NodeTransform[];
|
||||||
|
|
||||||
|
export declare function parse(template: string, options?: ParserOptions): RootNode;
|
||||||
|
|
||||||
|
export declare const parserOptions: ParserOptions;
|
||||||
|
|
||||||
|
export declare const transformStyle: NodeTransform;
|
||||||
|
|
||||||
|
export declare const TRANSITION: unique symbol;
|
||||||
|
|
||||||
|
export declare const TRANSITION_GROUP: unique symbol;
|
||||||
|
|
||||||
|
export declare const V_MODEL_CHECKBOX: unique symbol;
|
||||||
|
|
||||||
|
export declare const V_MODEL_DYNAMIC: unique symbol;
|
||||||
|
|
||||||
|
export declare const V_MODEL_RADIO: unique symbol;
|
||||||
|
|
||||||
|
export declare const V_MODEL_SELECT: unique symbol;
|
||||||
|
|
||||||
|
export declare const V_MODEL_TEXT: unique symbol;
|
||||||
|
|
||||||
|
export declare const V_ON_WITH_KEYS: unique symbol;
|
||||||
|
|
||||||
|
export declare const V_ON_WITH_MODIFIERS: unique symbol;
|
||||||
|
|
||||||
|
export declare const V_SHOW: unique symbol;
|
||||||
|
|
||||||
|
|
||||||
|
export * from "@vue/compiler-core";
|
||||||
|
|
||||||
|
export { }
|
5492
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.esm-browser.js
generated
vendored
Normal file
5492
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.esm-browser.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.esm-browser.prod.js
generated
vendored
Normal file
1
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.esm-browser.prod.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
483
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.esm-bundler.js
generated
vendored
Normal file
483
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.esm-bundler.js
generated
vendored
Normal file
|
@ -0,0 +1,483 @@
|
||||||
|
import { registerRuntimeHelpers, isBuiltInType, createSimpleExpression, createCompilerError, createObjectProperty, getConstantType, createCallExpression, TO_DISPLAY_STRING, transformModel as transformModel$1, findProp, hasDynamicKeyVBind, transformOn as transformOn$1, createCompoundExpression, isStaticExp, checkCompatEnabled, noopDirectiveTransform, baseCompile, baseParse } from '@vue/compiler-core';
|
||||||
|
export * from '@vue/compiler-core';
|
||||||
|
import { isVoidTag, isHTMLTag, isSVGTag, makeMap, parseStringStyle, capitalize, extend } from '@vue/shared';
|
||||||
|
|
||||||
|
const V_MODEL_RADIO = Symbol((process.env.NODE_ENV !== 'production') ? `vModelRadio` : ``);
|
||||||
|
const V_MODEL_CHECKBOX = Symbol((process.env.NODE_ENV !== 'production') ? `vModelCheckbox` : ``);
|
||||||
|
const V_MODEL_TEXT = Symbol((process.env.NODE_ENV !== 'production') ? `vModelText` : ``);
|
||||||
|
const V_MODEL_SELECT = Symbol((process.env.NODE_ENV !== 'production') ? `vModelSelect` : ``);
|
||||||
|
const V_MODEL_DYNAMIC = Symbol((process.env.NODE_ENV !== 'production') ? `vModelDynamic` : ``);
|
||||||
|
const V_ON_WITH_MODIFIERS = Symbol((process.env.NODE_ENV !== 'production') ? `vOnModifiersGuard` : ``);
|
||||||
|
const V_ON_WITH_KEYS = Symbol((process.env.NODE_ENV !== 'production') ? `vOnKeysGuard` : ``);
|
||||||
|
const V_SHOW = Symbol((process.env.NODE_ENV !== 'production') ? `vShow` : ``);
|
||||||
|
const TRANSITION = Symbol((process.env.NODE_ENV !== 'production') ? `Transition` : ``);
|
||||||
|
const TRANSITION_GROUP = Symbol((process.env.NODE_ENV !== 'production') ? `TransitionGroup` : ``);
|
||||||
|
registerRuntimeHelpers({
|
||||||
|
[V_MODEL_RADIO]: `vModelRadio`,
|
||||||
|
[V_MODEL_CHECKBOX]: `vModelCheckbox`,
|
||||||
|
[V_MODEL_TEXT]: `vModelText`,
|
||||||
|
[V_MODEL_SELECT]: `vModelSelect`,
|
||||||
|
[V_MODEL_DYNAMIC]: `vModelDynamic`,
|
||||||
|
[V_ON_WITH_MODIFIERS]: `withModifiers`,
|
||||||
|
[V_ON_WITH_KEYS]: `withKeys`,
|
||||||
|
[V_SHOW]: `vShow`,
|
||||||
|
[TRANSITION]: `Transition`,
|
||||||
|
[TRANSITION_GROUP]: `TransitionGroup`
|
||||||
|
});
|
||||||
|
|
||||||
|
/* eslint-disable no-restricted-globals */
|
||||||
|
let decoder;
|
||||||
|
function decodeHtmlBrowser(raw, asAttr = false) {
|
||||||
|
if (!decoder) {
|
||||||
|
decoder = document.createElement('div');
|
||||||
|
}
|
||||||
|
if (asAttr) {
|
||||||
|
decoder.innerHTML = `<div foo="${raw.replace(/"/g, '"')}">`;
|
||||||
|
return decoder.children[0].getAttribute('foo');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
decoder.innerHTML = raw;
|
||||||
|
return decoder.textContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isRawTextContainer = /*#__PURE__*/ makeMap('style,iframe,script,noscript', true);
|
||||||
|
const parserOptions = {
|
||||||
|
isVoidTag,
|
||||||
|
isNativeTag: tag => isHTMLTag(tag) || isSVGTag(tag),
|
||||||
|
isPreTag: tag => tag === 'pre',
|
||||||
|
decodeEntities: decodeHtmlBrowser ,
|
||||||
|
isBuiltInComponent: (tag) => {
|
||||||
|
if (isBuiltInType(tag, `Transition`)) {
|
||||||
|
return TRANSITION;
|
||||||
|
}
|
||||||
|
else if (isBuiltInType(tag, `TransitionGroup`)) {
|
||||||
|
return TRANSITION_GROUP;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// https://html.spec.whatwg.org/multipage/parsing.html#tree-construction-dispatcher
|
||||||
|
getNamespace(tag, parent) {
|
||||||
|
let ns = parent ? parent.ns : 0 /* HTML */;
|
||||||
|
if (parent && ns === 2 /* MATH_ML */) {
|
||||||
|
if (parent.tag === 'annotation-xml') {
|
||||||
|
if (tag === 'svg') {
|
||||||
|
return 1 /* SVG */;
|
||||||
|
}
|
||||||
|
if (parent.props.some(a => a.type === 6 /* ATTRIBUTE */ &&
|
||||||
|
a.name === 'encoding' &&
|
||||||
|
a.value != null &&
|
||||||
|
(a.value.content === 'text/html' ||
|
||||||
|
a.value.content === 'application/xhtml+xml'))) {
|
||||||
|
ns = 0 /* HTML */;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (/^m(?:[ions]|text)$/.test(parent.tag) &&
|
||||||
|
tag !== 'mglyph' &&
|
||||||
|
tag !== 'malignmark') {
|
||||||
|
ns = 0 /* HTML */;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (parent && ns === 1 /* SVG */) {
|
||||||
|
if (parent.tag === 'foreignObject' ||
|
||||||
|
parent.tag === 'desc' ||
|
||||||
|
parent.tag === 'title') {
|
||||||
|
ns = 0 /* HTML */;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ns === 0 /* HTML */) {
|
||||||
|
if (tag === 'svg') {
|
||||||
|
return 1 /* SVG */;
|
||||||
|
}
|
||||||
|
if (tag === 'math') {
|
||||||
|
return 2 /* MATH_ML */;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ns;
|
||||||
|
},
|
||||||
|
// https://html.spec.whatwg.org/multipage/parsing.html#parsing-html-fragments
|
||||||
|
getTextMode({ tag, ns }) {
|
||||||
|
if (ns === 0 /* HTML */) {
|
||||||
|
if (tag === 'textarea' || tag === 'title') {
|
||||||
|
return 1 /* RCDATA */;
|
||||||
|
}
|
||||||
|
if (isRawTextContainer(tag)) {
|
||||||
|
return 2 /* RAWTEXT */;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0 /* DATA */;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse inline CSS strings for static style attributes into an object.
|
||||||
|
// This is a NodeTransform since it works on the static `style` attribute and
|
||||||
|
// converts it into a dynamic equivalent:
|
||||||
|
// style="color: red" -> :style='{ "color": "red" }'
|
||||||
|
// It is then processed by `transformElement` and included in the generated
|
||||||
|
// props.
|
||||||
|
const transformStyle = node => {
|
||||||
|
if (node.type === 1 /* ELEMENT */) {
|
||||||
|
node.props.forEach((p, i) => {
|
||||||
|
if (p.type === 6 /* ATTRIBUTE */ && p.name === 'style' && p.value) {
|
||||||
|
// replace p with an expression node
|
||||||
|
node.props[i] = {
|
||||||
|
type: 7 /* DIRECTIVE */,
|
||||||
|
name: `bind`,
|
||||||
|
arg: createSimpleExpression(`style`, true, p.loc),
|
||||||
|
exp: parseInlineCSS(p.value.content, p.loc),
|
||||||
|
modifiers: [],
|
||||||
|
loc: p.loc
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const parseInlineCSS = (cssText, loc) => {
|
||||||
|
const normalized = parseStringStyle(cssText);
|
||||||
|
return createSimpleExpression(JSON.stringify(normalized), false, loc, 3 /* CAN_STRINGIFY */);
|
||||||
|
};
|
||||||
|
|
||||||
|
function createDOMCompilerError(code, loc) {
|
||||||
|
return createCompilerError(code, loc, (process.env.NODE_ENV !== 'production') || !true ? DOMErrorMessages : undefined);
|
||||||
|
}
|
||||||
|
const DOMErrorMessages = {
|
||||||
|
[50 /* X_V_HTML_NO_EXPRESSION */]: `v-html is missing expression.`,
|
||||||
|
[51 /* X_V_HTML_WITH_CHILDREN */]: `v-html will override element children.`,
|
||||||
|
[52 /* X_V_TEXT_NO_EXPRESSION */]: `v-text is missing expression.`,
|
||||||
|
[53 /* X_V_TEXT_WITH_CHILDREN */]: `v-text will override element children.`,
|
||||||
|
[54 /* X_V_MODEL_ON_INVALID_ELEMENT */]: `v-model can only be used on <input>, <textarea> and <select> elements.`,
|
||||||
|
[55 /* X_V_MODEL_ARG_ON_ELEMENT */]: `v-model argument is not supported on plain elements.`,
|
||||||
|
[56 /* X_V_MODEL_ON_FILE_INPUT_ELEMENT */]: `v-model cannot be used on file inputs since they are read-only. Use a v-on:change listener instead.`,
|
||||||
|
[57 /* X_V_MODEL_UNNECESSARY_VALUE */]: `Unnecessary value binding used alongside v-model. It will interfere with v-model's behavior.`,
|
||||||
|
[58 /* X_V_SHOW_NO_EXPRESSION */]: `v-show is missing expression.`,
|
||||||
|
[59 /* X_TRANSITION_INVALID_CHILDREN */]: `<Transition> expects exactly one child element or component.`,
|
||||||
|
[60 /* X_IGNORED_SIDE_EFFECT_TAG */]: `Tags with side effect (<script> and <style>) are ignored in client component templates.`
|
||||||
|
};
|
||||||
|
|
||||||
|
const transformVHtml = (dir, node, context) => {
|
||||||
|
const { exp, loc } = dir;
|
||||||
|
if (!exp) {
|
||||||
|
context.onError(createDOMCompilerError(50 /* X_V_HTML_NO_EXPRESSION */, loc));
|
||||||
|
}
|
||||||
|
if (node.children.length) {
|
||||||
|
context.onError(createDOMCompilerError(51 /* X_V_HTML_WITH_CHILDREN */, loc));
|
||||||
|
node.children.length = 0;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
props: [
|
||||||
|
createObjectProperty(createSimpleExpression(`innerHTML`, true, loc), exp || createSimpleExpression('', true))
|
||||||
|
]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const transformVText = (dir, node, context) => {
|
||||||
|
const { exp, loc } = dir;
|
||||||
|
if (!exp) {
|
||||||
|
context.onError(createDOMCompilerError(52 /* X_V_TEXT_NO_EXPRESSION */, loc));
|
||||||
|
}
|
||||||
|
if (node.children.length) {
|
||||||
|
context.onError(createDOMCompilerError(53 /* X_V_TEXT_WITH_CHILDREN */, loc));
|
||||||
|
node.children.length = 0;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
props: [
|
||||||
|
createObjectProperty(createSimpleExpression(`textContent`, true), exp
|
||||||
|
? getConstantType(exp, context) > 0
|
||||||
|
? exp
|
||||||
|
: createCallExpression(context.helperString(TO_DISPLAY_STRING), [exp], loc)
|
||||||
|
: createSimpleExpression('', true))
|
||||||
|
]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const transformModel = (dir, node, context) => {
|
||||||
|
const baseResult = transformModel$1(dir, node, context);
|
||||||
|
// base transform has errors OR component v-model (only need props)
|
||||||
|
if (!baseResult.props.length || node.tagType === 1 /* COMPONENT */) {
|
||||||
|
return baseResult;
|
||||||
|
}
|
||||||
|
if (dir.arg) {
|
||||||
|
context.onError(createDOMCompilerError(55 /* X_V_MODEL_ARG_ON_ELEMENT */, dir.arg.loc));
|
||||||
|
}
|
||||||
|
function checkDuplicatedValue() {
|
||||||
|
const value = findProp(node, 'value');
|
||||||
|
if (value) {
|
||||||
|
context.onError(createDOMCompilerError(57 /* X_V_MODEL_UNNECESSARY_VALUE */, value.loc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { tag } = node;
|
||||||
|
const isCustomElement = context.isCustomElement(tag);
|
||||||
|
if (tag === 'input' ||
|
||||||
|
tag === 'textarea' ||
|
||||||
|
tag === 'select' ||
|
||||||
|
isCustomElement) {
|
||||||
|
let directiveToUse = V_MODEL_TEXT;
|
||||||
|
let isInvalidType = false;
|
||||||
|
if (tag === 'input' || isCustomElement) {
|
||||||
|
const type = findProp(node, `type`);
|
||||||
|
if (type) {
|
||||||
|
if (type.type === 7 /* DIRECTIVE */) {
|
||||||
|
// :type="foo"
|
||||||
|
directiveToUse = V_MODEL_DYNAMIC;
|
||||||
|
}
|
||||||
|
else if (type.value) {
|
||||||
|
switch (type.value.content) {
|
||||||
|
case 'radio':
|
||||||
|
directiveToUse = V_MODEL_RADIO;
|
||||||
|
break;
|
||||||
|
case 'checkbox':
|
||||||
|
directiveToUse = V_MODEL_CHECKBOX;
|
||||||
|
break;
|
||||||
|
case 'file':
|
||||||
|
isInvalidType = true;
|
||||||
|
context.onError(createDOMCompilerError(56 /* X_V_MODEL_ON_FILE_INPUT_ELEMENT */, dir.loc));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// text type
|
||||||
|
(process.env.NODE_ENV !== 'production') && checkDuplicatedValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (hasDynamicKeyVBind(node)) {
|
||||||
|
// element has bindings with dynamic keys, which can possibly contain
|
||||||
|
// "type".
|
||||||
|
directiveToUse = V_MODEL_DYNAMIC;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// text type
|
||||||
|
(process.env.NODE_ENV !== 'production') && checkDuplicatedValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tag === 'select') {
|
||||||
|
directiveToUse = V_MODEL_SELECT;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// textarea
|
||||||
|
(process.env.NODE_ENV !== 'production') && checkDuplicatedValue();
|
||||||
|
}
|
||||||
|
// inject runtime directive
|
||||||
|
// by returning the helper symbol via needRuntime
|
||||||
|
// the import will replaced a resolveDirective call.
|
||||||
|
if (!isInvalidType) {
|
||||||
|
baseResult.needRuntime = context.helper(directiveToUse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
context.onError(createDOMCompilerError(54 /* X_V_MODEL_ON_INVALID_ELEMENT */, dir.loc));
|
||||||
|
}
|
||||||
|
// native vmodel doesn't need the `modelValue` props since they are also
|
||||||
|
// passed to the runtime as `binding.value`. removing it reduces code size.
|
||||||
|
baseResult.props = baseResult.props.filter(p => !(p.key.type === 4 /* SIMPLE_EXPRESSION */ &&
|
||||||
|
p.key.content === 'modelValue'));
|
||||||
|
return baseResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isEventOptionModifier = /*#__PURE__*/ makeMap(`passive,once,capture`);
|
||||||
|
const isNonKeyModifier = /*#__PURE__*/ makeMap(
|
||||||
|
// event propagation management
|
||||||
|
`stop,prevent,self,` +
|
||||||
|
// system modifiers + exact
|
||||||
|
`ctrl,shift,alt,meta,exact,` +
|
||||||
|
// mouse
|
||||||
|
`middle`);
|
||||||
|
// left & right could be mouse or key modifiers based on event type
|
||||||
|
const maybeKeyModifier = /*#__PURE__*/ makeMap('left,right');
|
||||||
|
const isKeyboardEvent = /*#__PURE__*/ makeMap(`onkeyup,onkeydown,onkeypress`, true);
|
||||||
|
const resolveModifiers = (key, modifiers, context, loc) => {
|
||||||
|
const keyModifiers = [];
|
||||||
|
const nonKeyModifiers = [];
|
||||||
|
const eventOptionModifiers = [];
|
||||||
|
for (let i = 0; i < modifiers.length; i++) {
|
||||||
|
const modifier = modifiers[i];
|
||||||
|
if (modifier === 'native' &&
|
||||||
|
checkCompatEnabled("COMPILER_V_ON_NATIVE" /* COMPILER_V_ON_NATIVE */, context, loc)) {
|
||||||
|
eventOptionModifiers.push(modifier);
|
||||||
|
}
|
||||||
|
else if (isEventOptionModifier(modifier)) {
|
||||||
|
// eventOptionModifiers: modifiers for addEventListener() options,
|
||||||
|
// e.g. .passive & .capture
|
||||||
|
eventOptionModifiers.push(modifier);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// runtimeModifiers: modifiers that needs runtime guards
|
||||||
|
if (maybeKeyModifier(modifier)) {
|
||||||
|
if (isStaticExp(key)) {
|
||||||
|
if (isKeyboardEvent(key.content)) {
|
||||||
|
keyModifiers.push(modifier);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nonKeyModifiers.push(modifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
keyModifiers.push(modifier);
|
||||||
|
nonKeyModifiers.push(modifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (isNonKeyModifier(modifier)) {
|
||||||
|
nonKeyModifiers.push(modifier);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
keyModifiers.push(modifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
keyModifiers,
|
||||||
|
nonKeyModifiers,
|
||||||
|
eventOptionModifiers
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const transformClick = (key, event) => {
|
||||||
|
const isStaticClick = isStaticExp(key) && key.content.toLowerCase() === 'onclick';
|
||||||
|
return isStaticClick
|
||||||
|
? createSimpleExpression(event, true)
|
||||||
|
: key.type !== 4 /* SIMPLE_EXPRESSION */
|
||||||
|
? createCompoundExpression([
|
||||||
|
`(`,
|
||||||
|
key,
|
||||||
|
`) === "onClick" ? "${event}" : (`,
|
||||||
|
key,
|
||||||
|
`)`
|
||||||
|
])
|
||||||
|
: key;
|
||||||
|
};
|
||||||
|
const transformOn = (dir, node, context) => {
|
||||||
|
return transformOn$1(dir, node, context, baseResult => {
|
||||||
|
const { modifiers } = dir;
|
||||||
|
if (!modifiers.length)
|
||||||
|
return baseResult;
|
||||||
|
let { key, value: handlerExp } = baseResult.props[0];
|
||||||
|
const { keyModifiers, nonKeyModifiers, eventOptionModifiers } = resolveModifiers(key, modifiers, context, dir.loc);
|
||||||
|
// normalize click.right and click.middle since they don't actually fire
|
||||||
|
if (nonKeyModifiers.includes('right')) {
|
||||||
|
key = transformClick(key, `onContextmenu`);
|
||||||
|
}
|
||||||
|
if (nonKeyModifiers.includes('middle')) {
|
||||||
|
key = transformClick(key, `onMouseup`);
|
||||||
|
}
|
||||||
|
if (nonKeyModifiers.length) {
|
||||||
|
handlerExp = createCallExpression(context.helper(V_ON_WITH_MODIFIERS), [
|
||||||
|
handlerExp,
|
||||||
|
JSON.stringify(nonKeyModifiers)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (keyModifiers.length &&
|
||||||
|
// if event name is dynamic, always wrap with keys guard
|
||||||
|
(!isStaticExp(key) || isKeyboardEvent(key.content))) {
|
||||||
|
handlerExp = createCallExpression(context.helper(V_ON_WITH_KEYS), [
|
||||||
|
handlerExp,
|
||||||
|
JSON.stringify(keyModifiers)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (eventOptionModifiers.length) {
|
||||||
|
const modifierPostfix = eventOptionModifiers.map(capitalize).join('');
|
||||||
|
key = isStaticExp(key)
|
||||||
|
? createSimpleExpression(`${key.content}${modifierPostfix}`, true)
|
||||||
|
: createCompoundExpression([`(`, key, `) + "${modifierPostfix}"`]);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
props: [createObjectProperty(key, handlerExp)]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const transformShow = (dir, node, context) => {
|
||||||
|
const { exp, loc } = dir;
|
||||||
|
if (!exp) {
|
||||||
|
context.onError(createDOMCompilerError(58 /* X_V_SHOW_NO_EXPRESSION */, loc));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
props: [],
|
||||||
|
needRuntime: context.helper(V_SHOW)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const transformTransition = (node, context) => {
|
||||||
|
if (node.type === 1 /* ELEMENT */ &&
|
||||||
|
node.tagType === 1 /* COMPONENT */) {
|
||||||
|
const component = context.isBuiltInComponent(node.tag);
|
||||||
|
if (component === TRANSITION) {
|
||||||
|
return () => {
|
||||||
|
if (!node.children.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// warn multiple transition children
|
||||||
|
if (hasMultipleChildren(node)) {
|
||||||
|
context.onError(createDOMCompilerError(59 /* X_TRANSITION_INVALID_CHILDREN */, {
|
||||||
|
start: node.children[0].loc.start,
|
||||||
|
end: node.children[node.children.length - 1].loc.end,
|
||||||
|
source: ''
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// check if it's s single child w/ v-show
|
||||||
|
// if yes, inject "persisted: true" to the transition props
|
||||||
|
const child = node.children[0];
|
||||||
|
if (child.type === 1 /* ELEMENT */) {
|
||||||
|
for (const p of child.props) {
|
||||||
|
if (p.type === 7 /* DIRECTIVE */ && p.name === 'show') {
|
||||||
|
node.props.push({
|
||||||
|
type: 6 /* ATTRIBUTE */,
|
||||||
|
name: 'persisted',
|
||||||
|
value: undefined,
|
||||||
|
loc: node.loc
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function hasMultipleChildren(node) {
|
||||||
|
// #1352 filter out potential comment nodes.
|
||||||
|
const children = (node.children = node.children.filter(c => c.type !== 3 /* COMMENT */ &&
|
||||||
|
!(c.type === 2 /* TEXT */ && !c.content.trim())));
|
||||||
|
const child = children[0];
|
||||||
|
return (children.length !== 1 ||
|
||||||
|
child.type === 11 /* FOR */ ||
|
||||||
|
(child.type === 9 /* IF */ && child.branches.some(hasMultipleChildren)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ignoreSideEffectTags = (node, context) => {
|
||||||
|
if (node.type === 1 /* ELEMENT */ &&
|
||||||
|
node.tagType === 0 /* ELEMENT */ &&
|
||||||
|
(node.tag === 'script' || node.tag === 'style')) {
|
||||||
|
context.onError(createDOMCompilerError(60 /* X_IGNORED_SIDE_EFFECT_TAG */, node.loc));
|
||||||
|
context.removeNode();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const DOMNodeTransforms = [
|
||||||
|
transformStyle,
|
||||||
|
...((process.env.NODE_ENV !== 'production') ? [transformTransition] : [])
|
||||||
|
];
|
||||||
|
const DOMDirectiveTransforms = {
|
||||||
|
cloak: noopDirectiveTransform,
|
||||||
|
html: transformVHtml,
|
||||||
|
text: transformVText,
|
||||||
|
model: transformModel,
|
||||||
|
on: transformOn,
|
||||||
|
show: transformShow
|
||||||
|
};
|
||||||
|
function compile(template, options = {}) {
|
||||||
|
return baseCompile(template, extend({}, parserOptions, options, {
|
||||||
|
nodeTransforms: [
|
||||||
|
// ignore <script> and <tag>
|
||||||
|
// this is not put inside DOMNodeTransforms because that list is used
|
||||||
|
// by compiler-ssr to generate vnode fallback branches
|
||||||
|
ignoreSideEffectTags,
|
||||||
|
...DOMNodeTransforms,
|
||||||
|
...(options.nodeTransforms || [])
|
||||||
|
],
|
||||||
|
directiveTransforms: extend({}, DOMDirectiveTransforms, options.directiveTransforms || {}),
|
||||||
|
transformHoist: null
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
function parse(template, options = {}) {
|
||||||
|
return baseParse(template, extend({}, parserOptions, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
export { DOMDirectiveTransforms, DOMNodeTransforms, TRANSITION, TRANSITION_GROUP, V_MODEL_CHECKBOX, V_MODEL_DYNAMIC, V_MODEL_RADIO, V_MODEL_SELECT, V_MODEL_TEXT, V_ON_WITH_KEYS, V_ON_WITH_MODIFIERS, V_SHOW, compile, createDOMCompilerError, parse, parserOptions, transformStyle };
|
5642
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.global.js
generated
vendored
Normal file
5642
webui/node_modules/@vue/compiler-dom/dist/compiler-dom.global.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue