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