1
+ function Set-Neocities
2
+ {
3
+ <#
4
+ . SYNOPSIS
5
+ Sets Neocities files
6
+ . DESCRIPTION
7
+ Sets files on Neocities website using the neocities API.
8
+ #>
9
+ [CmdletBinding (DefaultParameterSetName = ' upload' )]
10
+ param (
11
+ # The path to the file to upload, or a dictionary of files and their contents.
12
+ [Parameter (ValueFromPipelineByPropertyName )]
13
+ [Alias (' Fullname' , ' FilePath' , ' Path' )]
14
+ [PSObject ]
15
+ $File ,
16
+
17
+ # The neocities credential
18
+ [Parameter (ValueFromPipelineByPropertyName )]
19
+ [Alias (
20
+ ' Credentials' , # Plural aliases are nice
21
+ ' PSCredential' , # so are parameters that match the type name.
22
+ ' NeocitiesCredential' , # A contextual alias is a good idea, too.
23
+ ' NeocitiesCredentials' # And you may need to pluralize that contextual alias.
24
+ )]
25
+ [PSCredential ]
26
+ $Credential ,
27
+
28
+ # The access token
29
+ [Parameter (ValueFromPipelineByPropertyName )]
30
+ [string ]
31
+ $AccessToken
32
+ )
33
+
34
+ begin {
35
+ $NeocitiesApi = " https://neocities.org/api"
36
+ $multiparts = [Ordered ]@ {}
37
+ $boundary = " boundary"
38
+ $contentType = " multipart/form-data; boundary=`" $boundary `" "
39
+
40
+ }
41
+ process {
42
+ $parameterSet = $PSCmdlet.ParameterSetName
43
+ $psuedoNamespace = " neocities"
44
+ $pseudoType = " $parameterSet "
45
+ $InvokeSplat = [Ordered ]@ {
46
+ Uri = " $NeocitiesApi " , $PSCmdlet.ParameterSetName -join ' /'
47
+ }
48
+
49
+ # If an access token was provided
50
+ if ($AccessToken )
51
+ {
52
+ # use it
53
+ $InvokeSplat.Headers = @ {Authorization = " Bearer $AccessToken " }
54
+ # and cache it for later use
55
+ $script :NeocitiesAccessToken = $AccessToken
56
+ }
57
+ elseif ($Credential )
58
+ {
59
+ # If a credential was provided, use it
60
+ $InvokeSplat.Credential = $Credential
61
+ # and cache it for later use
62
+ $script :NeoCitiesCredential = $Credential
63
+ # (don't forget to set authentication to basic)
64
+ $InvokeSplat.Authentication = ' Basic'
65
+ }
66
+ elseif ($script :NeocitiesAccessToken ) {
67
+ # If we had a cached access token, use it
68
+ $InvokeSplat.Headers = @ {Authorization = " Bearer $ ( $script :NeocitiesAccessToken ) " }
69
+ }
70
+ elseif ($script :NeoCitiesCredential ) {
71
+ # If we had a cached credential, use it
72
+ $InvokeSplat.Credential = $script :NeoCitiesCredential
73
+ # and don't forget to set authentication to basic.
74
+ $InvokeSplat.Authentication = ' Basic'
75
+ }
76
+
77
+ # If neither an access token nor a credential was provided, we can't do anything.
78
+ if (-not $InvokeSplat.Credential -and -not $InvokeSplat.Headers )
79
+ {
80
+ # so error out.
81
+ Write-Error " No -Credential provided"
82
+ return
83
+ }
84
+
85
+ $InvokeSplat.ContentType = $contentType
86
+
87
+ # If we were piped in a file
88
+ if ($_ -is [IO.FileInfo ]) {
89
+ $file = $_ # set the parameter directly
90
+ }
91
+
92
+ # For every file passed in, we need to make a unique request.
93
+ foreach ($fileInfo in $file ) {
94
+ # If this is a file, this is easy
95
+ if ($fileInfo -is [IO.FileInfo ]) {
96
+ # just get the string representation of the file's bytes and add them to the multipart collection
97
+ $multiparts [$file.Name ] = $OutputEncoding.GetString ([IO.File ]::ReadAllBytes($file ))
98
+ }
99
+ # If the file was a path, we need to get the file's contents
100
+ elseif ($fileInfo -is [string ] -and (Test-Path $fileInfo )) {
101
+ $multiparts [$fileInfo ] = Get-Content - Raw $fileInfo
102
+ }
103
+ # If the file was a dictionary, treat each key as a file name and each value as the file's contents
104
+ elseif ($fileInfo -is [Collections.IDictionary ]) {
105
+ foreach ($keyValuePair in $fileInfo.GetEnumerator ()) {
106
+ # If the value is a byte array, convert it to a string
107
+ if ($keyValuePair.Value -is [byte []]) {
108
+ $multiparts [$keyValuePair.Key ] = $OutputEncoding.GetString ($keyValuePair.Value )
109
+ }
110
+ # If the value is a file, read the file's bytes and convert them to a string
111
+ elseif ($keyValuePair.Value -is [IO.FileInfo ]) {
112
+ $multiparts [$keyValuePair.Key ] = $OutputEncoding.GetString ([IO.File ]::ReadAllBytes($keyValuePair.Value ))
113
+ }
114
+ # If the value is a pth to file, read the file's bytes and convert them to a string
115
+ elseif ($keyValuePair.Value -is [string ] -and (Test-Path $keyValuePair.Value )) {
116
+ $multiparts [$keyValuePair.Key ] = Get-Content - Raw $keyValuePair.Value
117
+ }
118
+ # last but not least, stringify the value and add it to the collection
119
+ else
120
+ {
121
+ $multiparts [$keyValuePair.Key ] = " $ ( $keyValuePair.Value ) "
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ }
128
+
129
+ end {
130
+ # Despite the content type being multipart, we can actually only send one part at a time:
131
+
132
+ # Any way we slice it, we'll need to POST the data to the API.
133
+ $InvokeSplat.Method = ' POST'
134
+
135
+
136
+ # For each part we've found
137
+ foreach ($filePart in $multiparts.GetEnumerator ()) {
138
+ $InvokeSplat.Body = @ (
139
+ # Create a bounary
140
+ " --$boundary "
141
+ # Add the file name and content type to the header
142
+ " Content-Disposition: form-data; name=`" $ ( $filePart.Key ) `" ; filename=`" $ ( $filePart.Key ) `" "
143
+ # We're always uploading this a text/plain with neocities, and we need to set the encoding to whatever we passed.
144
+ " Content-Type: text/plain; charset=$ ( $OutputEncoding.WebName ) "
145
+ # The bounary MIME data must be followed by a newline
146
+ " `r`n "
147
+ # followed by the file contents
148
+ $filePart.Value
149
+ # followed by an additional boundary
150
+ " --$boundary --"
151
+ ) -join " `r`n " # (all of these pieces are joined by a newline)
152
+
153
+ # If -WhatIf was passed, don't actually upload the file, just show the splatted parameters.
154
+ if ($WhatIfPreference ) {
155
+ # (Remove the headers and credential from the splatted parameters, so we don't leak any sensitive information)
156
+ $InvokeSplat.Remove (' Headers' )
157
+ $InvokeSplat.Remove (' Credential' )
158
+ $InvokeSplat
159
+ continue
160
+ }
161
+
162
+ # Invoke-RestMethod with our splatted parameters, and we'll upload the file.
163
+ Invoke-RestMethod @InvokeSplat
164
+ }
165
+ }
166
+ }
0 commit comments